【问题标题】:Difference between g++ and clang++ with enable_ifg++ 和 clang++ 与 enable_if 的区别
【发布时间】:2020-03-12 09:25:33
【问题描述】:

我想编写一个函数,它返回 T 类型的实例,但根据 T 的构造方式,其行为会有所不同。假设我有这样的结构

#include <type_traits>                                                                                                                                                                                                                                                                                                        
#include <iostream>

struct A {}; 
struct B {}; 
struct C { 
  C(A a) {
    std::cout << "C" << std::endl;
  }
};

我想通过给 Cs 一个 A 来创建它们。我有一个类似这样的结构,它使用 enable_if 来选择两个函数之一:

struct E {

  template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
  C get() {
    return C{A{}};
  }
  template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
  C get() {
    return C{B{}};
  }
};

这可以用 g++82 编译(我认为也是 g++9),但是 clang9 给了我错误

$ clang++ --std=c++17 main.cpp 
main.cpp:26:12: error: no matching constructor for initialization of 'C'
    return C{B{}};
           ^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
       ^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
       ^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
  C(A a) {
  ^
1 error generated.

即使 enable_if 应该隐藏该功能。 (我打电话给E e; auto c = e.get();)。如果我不对 C 进行硬编码,而是使用模板来传入 C,那么它在两个编译器中都可以工作。

template<typename T>
struct F {

  template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
  T get() {
    return T{A{}};
  }
  template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
  T get() {
    return T{B{}};
  }
};

我不明白为什么 clang 显然会对函数的主体进行类型检查,即使该函数应该被 enable_if 禁用。

【问题讨论】:

    标签: c++ clang++ enable-if


    【解决方案1】:

    两个编译器都是对的,

    http://eel.is/c++draft/temp.res#8.1

    可以在任何实例化之前检查模板的有效性。 [注意:知道哪些名称是类型名称允许以这种方式检查每个模板的语法。 — end note ] 程序格式错误,不需要诊断,如果:

    (8.1)
    - 无法为模板或 constexpr 的子语句生成有效的特化,如果模板内的语句和模板未实例化,或

    [..]

    (8.4)
    - 由于不依赖于模板参数的构造,紧随其定义的模板的假设实例化将是格式错误的,或者

    return C{B{}}; 不依赖于模板,并且是错误的。通过诊断问题,clang 很好。

    【讨论】:

    • 第 8.4 段也适用。
    【解决方案2】:

    由于您似乎可以访问支持 c++17 的编译器,因此您可以使用 if constexpr 而不是 enable_if 来完成您想要的操作。

    #include <iostream>
    #include <type_traits>
    
    struct A {};
    
    struct B {};
    
    struct C {
        explicit C(A a) {
            std::cout << "C" << std::endl;
        }
    };
    
    template<typename T>
    struct False : std::false_type {};
    
    struct E {
        template<typename T = void>
        C get() const {
            if constexpr (std::is_constructible_v<C, A>) {
                return C{A{}};
            } else if constexpr (std::is_constructible_v<C, B>) {
                return C{B{}};
            } else {
                static_assert(False<T>::value, "Error");
            }
        }
    };
    
    int main() {
        const auto C{E{}.get()};
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-29
      • 1970-01-01
      • 1970-01-01
      • 2014-03-11
      • 2011-08-23
      • 2020-09-30
      • 2016-04-07
      • 2013-03-30
      相关资源
      最近更新 更多