首页 文章

通过类模板参数跨平台选择运算符重载

提问于
浏览
2

我正在尝试编写一个模板化的类,它本质上只是其他类型的包装器,以便能够轻松地观察值发生的事情 - 每次调用任何运算符,构造函数,析构函数等 . 所以我试图通过在没有运算符bool()的任何类上有一个static_cast的类上调用运算符bool()来创建一个运算符bool() . 如果有人试图转换为bool,那么无法执行任何操作的类应该只会失败编译 . 我还没有专注于这一部分,因为我被这个更简单的案例所困扰 .

它在MSVC 2015上编译并正常工作,但不会在Clang(3.8.1)或GCC(6.2)上编译 . GCC和Clang都设置为 -std=c++1z

链接到在线编译器,显示GCC和Clang上的错误:https://godbolt.org/g/v3B6TE

这是一个显示问题的精简版本:

#include <type_traits>
#include <utility>
#include <assert.h>

template <typename T>
struct Wrapper {
    T val;

    template<typename...Params>
    Wrapper(Params&&...args): val(std::forward<Params>(args)...){}

    /**
    * \brief bool conversion operator
    */
    template <typename = std::enable_if_t<std::is_fundamental<T>::value>>
    operator bool() const {
        return static_cast<bool>(val);
    }

    /**
    * \brief bool conversion operator
    */
    template <typename = std::enable_if_t<!std::is_fundamental<T>::value>, typename = void>
    operator bool() const {
        return val.operator bool();
    }
};


struct HasOperatorBool {
    mutable bool done{ false };

    operator bool() const{
        done = true;
        return done;
    }
};

int main(int argc, char** argv) {
    Wrapper<HasOperatorBool> whob;
    bool didIt = whob;
    assert(didIt);

    Wrapper<int> wi{1};
    bool bi = wi;
    assert(bi);

    return 0;
}

clang和gcc都说 std::enable_if_t 没有 type

GCC:

In file included from /tmp/gcc-explorer-compiler116910-70-16kehep/example.cpp:1:
/usr/lib/gcc/x86_64-linux-gnu/5.4.1/../../../../include/c++/5.4.1/type_traits:2388:44: error: no type named 'type' in 'std::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^~~~~
15 : note: in instantiation of template type alias 'enable_if_t' requested here
template <typename = std::enable_if_t<std::is_fundamental<T>::value>>
^
40 : note: in instantiation of template class 'Wrapper<HasOperatorBool>' requested here
Wrapper<HasOperatorBool> whob;
^
In file included from /tmp/gcc-explorer-compiler116910-70-16kehep/example.cpp:1:
/usr/lib/gcc/x86_64-linux-gnu/5.4.1/../../../../include/c++/5.4.1/type_traits:2388:44: error: no type named 'type' in 'std::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
using enable_if_t = typename enable_if<_Cond, _Tp>::type;
^~~~~
2 errors generated.
Compiler exited with result code 1

铛:

In file included from /tmp/gcc-explorer-compiler116910-70-1nlkefa/example.cpp:1:0:
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = std::is_fundamental<HasOperatorBool>::value; _Tp = void]':
16 : required from 'struct Wrapper<HasOperatorBool>'
40 : required from here
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/type_traits:2512:61: error: no type named 'type' in 'struct std::enable_if<false, void>'
using enable_if_t = typename enable_if<_Cond, _Tp>::type;

我可能对替代方法感兴趣,但我对技术原因非常感兴趣,为什么这不适用于3个编译器中的2个 . 一个或多个编译器是错误的吗?有没有办法实现我想要的?

1 回答

  • 3

    你实际上并没有做SFINAE . 当使用 std::enable_if<T> 时,有问题的 T 应该直接来自编译器试图实例化的模板(immediate-context) . 但由于 T 是您的类的模板参数,而不是方法,因此替换失败并给出硬错误而不是SFINAE . 我不确定VS的行为在这方面是否符合标准 .

    你想要的是引入一个默认为类模板参数的虚拟模板参数:

    /**
    * \brief bool conversion operator
    */
    template <typename X = T, typename = std::enable_if_t<std::is_fundamental<X>::value>>
    operator bool() const {
        return static_cast<bool>(val);
    }
    
    /**
    * \brief bool conversion operator
    */
    template <typename X = T, typename = std::enable_if_t<!std::is_fundamental<X>::value>, typename = void>
    operator bool() const {
        return val.operator bool();
    }
    

相关问题