仅在某些情况下实例化模板中使用 decltype 的函数定义

发布于 2024-10-29 05:39:09 字数 1207 浏览 1 评论 0原文

作为理解 C++0x 的练习,我试图创建一个 C++ 类,它包装某种模板化类型的指针:

template <typename T>
class Wrapper {
    T *t;
    /* ... */
};

在 Wrapper 类内部,我想公开 T 可能通过 Wrapper 实现的任何重载运算符班级。包装器本身只是将函数调用转发到底层 t 对象。

template <typename U>
auto operator+(U &u) -> decltype (*t + u) {
    return *t + u;
}

问题是我不希望 Wrapper 暴露 T 可能无法实现的运算符。例如,如果 T 没有实现operator+,那么Wrapper 也不应该暴露operator+。

在operator+(以及任何二元运算)的情况下,一切都会顺利进行,因为运算符必然成为模板函数,因此只有在我们尝试调用例如Wrapper::operator+ 时才会被实例化。

然而,对于一元运算符(例如,++),没有明确的方法来保护运算符,以便当且仅当 T 实现运算符++ 时才实例化它。例如,此类中operator++ 的简单实现

auto operator++() -> decltype(++(*t)) {
    return ++(*t);
}

无法针对不支持operator++() 的T 进行编译。

根据我对标准的理解,如果我们有以下使用 Wrapper 的代码

class X { };
Wrapper<X> w;

,我们将实例化 Wrapper 和 Wrapper::operator++() 的声明,但不会实例化其定义,除非我们调用它(或显式实例化它)。通常这是可以的,因为 X::operator++ 的使用仅出现在 Wrapper::operator++() 的定义中。但是,由于 decltype,我们在声明中使用了 X::operator++,以便类型检查器检查 X::operator++ 是否存在,从而失败。

我们是否可以定义operator++()(以及通常使用decltype的任何此类转发函数),并使其具有实例化的属性,前提是底层对象也支持operator++()?或者考虑到模板实例化和 decltype 的语义,这是不可能实现的吗?

As an exercise in understanding C++0x, I am trying to create a C++ class that wraps a pointer of some template-ized type:

template <typename T>
class Wrapper {
    T *t;
    /* ... */
};

Inside of the Wrapper class, I would like to expose any overloaded operators that T may implement through the Wrapper class. The wrapper itself simply forwards the function call to the underlying t object.

template <typename U>
auto operator+(U &u) -> decltype (*t + u) {
    return *t + u;
}

The catch is that I do not want Wrapper exposing operators that T may not implement. For example, if T does not implement operator+ then Wrapper should not expose operator+ as well.

In the case of operator+ (and any binary operation), everything works out because the operator necessarily becomes a template function and is thus only instantiated when we try to invoke, e.g., Wrapper::operator+.

However, in the case of unary operators (e.g., ++), there is not a clear way to guard the operator so that it is instantiated iff T implements operator++. For example, the naive implementation of operator++ in this class

auto operator++() -> decltype(++(*t)) {
    return ++(*t);
}

fails to compile for a T that does not support operator++().

From my understanding of the standard, if we have the following code that uses Wrapper

class X { };
Wrapper<X> w;

We will instantiate Wrapper and the declaration of Wrapper::operator++() but not its definition unless we invoke it (or explicitly instantiate it). Normally this would be ok, because the use of X::operator++ occurs only in the definition of Wrapper::operator++(). However, because of decltype, we use X::operator++ in the declaration so that the typechecker checks for the existence of X::operator++ and thus fails.

Can we define operator++() (and in general any such forwarding function that uses decltype) with the property that it is instantiated iff the underlying object also supports operator++()? Or given the semantics of template instantiation along with decltype, is this impossible to accomplish?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

似梦非梦 2024-11-05 05:39:09

您可以将运算符声明为非成员模板:

template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
    return ++*arg.t;
}

You can declare the operator as a non-member template:

template <typename T>
auto operator++(Wrapper<T>& arg) -> decltype(++*arg.t) {
    return ++*arg.t;
}
九公里浅绿 2024-11-05 05:39:09

您还可以使用默认模板参数做一些技巧,只是为了使运算符的操作数具有依赖性

template<typename Trick = T>
auto operator++() -> decltype(++(static_cast<Trick&>(*t))) {
    return ++(*t);
}

,也许在中间有一个辅助函数

template<typename /* Ignored */, typename T> T &&id(T &&t) {
    return std::forward<T>(t);
}

template<typename Void = void>
auto operator++() -> decltype(++(*id<Void>(t))) {
    return ++(*t);
}

You could also do tricks with default template arguments, just for making the operand of the operator be dependent

template<typename Trick = T>
auto operator++() -> decltype(++(static_cast<Trick&>(*t))) {
    return ++(*t);
}

Perhaps with a helper function in between

template<typename /* Ignored */, typename T> T &&id(T &&t) {
    return std::forward<T>(t);
}

template<typename Void = void>
auto operator++() -> decltype(++(*id<Void>(t))) {
    return ++(*t);
}
∞梦里开花 2024-11-05 05:39:09

如果您能弄清楚如何将 std::enable_if 加入到运算符签名中,这里有一个元函数,用于检查例如 operator-> 是否存在:

#include <type_traits>

template<typename T, typename R>
inline R* has_deref_opr_sfinae_impl_helper(R (T::*)()) { return 0; }

template<typename T, typename R>
inline R* has_deref_opr_sfinae_impl_helper(R (T::*)() const) { return 0; }

template<
    typename T,
    bool IsPointer =
        std::is_pointer<T>::value &&
        !std::is_same<
            typename std::remove_cv<
                typename std::remove_pointer<
                    typename std::remove_cv<T>::type
                >::type
            >::type,
            void
        >::value
>
class has_deref_opr
{
    template<
        typename U,
        typename R = decltype(has_deref_opr_sfinae_impl_helper(&U::operator->))
    >
    struct sfinae_impl { };

    typedef char true_t;
    struct false_t { true_t f[2]; };

    template<typename U>
    static true_t check(U*, sfinae_impl<U>* = 0);
    template<typename U>
    static false_t check(...);

public:
    static bool const value = sizeof(check<T>(0)) == sizeof(true_t);
};

template<typename T>
class has_deref_opr<T, true>
{
public:
    static bool const value = true;
};

一些注意事项:

  • 我用 GC 4.4.1 进行了测试,它不喜欢 has_deref_opr_sfinae_impl_helper 位于 has_deref_opr 内部,不知道为什么。也许这在最新版本的 GCC
  • VC++ 2010 SP1 中发生了变化,由于模板实例化错误,我无法找到解决方法,因此无法编译此错误:-[

希望这会有所帮助。

If you can figure out how to work std::enable_if into the operator signature, here's a metafunction that checks for the existence of e.g. operator->:

#include <type_traits>

template<typename T, typename R>
inline R* has_deref_opr_sfinae_impl_helper(R (T::*)()) { return 0; }

template<typename T, typename R>
inline R* has_deref_opr_sfinae_impl_helper(R (T::*)() const) { return 0; }

template<
    typename T,
    bool IsPointer =
        std::is_pointer<T>::value &&
        !std::is_same<
            typename std::remove_cv<
                typename std::remove_pointer<
                    typename std::remove_cv<T>::type
                >::type
            >::type,
            void
        >::value
>
class has_deref_opr
{
    template<
        typename U,
        typename R = decltype(has_deref_opr_sfinae_impl_helper(&U::operator->))
    >
    struct sfinae_impl { };

    typedef char true_t;
    struct false_t { true_t f[2]; };

    template<typename U>
    static true_t check(U*, sfinae_impl<U>* = 0);
    template<typename U>
    static false_t check(...);

public:
    static bool const value = sizeof(check<T>(0)) == sizeof(true_t);
};

template<typename T>
class has_deref_opr<T, true>
{
public:
    static bool const value = true;
};

A few notes:

  • I tested with GC 4.4.1, it didn't like has_deref_opr_sfinae_impl_helper being inside of has_deref_opr, not sure why. Maybe this is changed in more recent versions of GCC
  • VC++ 2010 SP1 fails to compile this due to a template instantiation bug that I couldn't find a workaround for :-[

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文