g ++和clang ++与enable_if的区别

问题描述 投票:0回答:2

我想编写一个函数,该函数返回类型T的实例,但其行为取决于如何构造T。说我有这样的结构

#include <type_traits>                                                                                                                                                                                                                                                                                                        
#include <iostream>

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

我想通过给它们一个A来创建C。我有一个类似的结构,因此可以使用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
2个回答
2
投票

两个编译器都正确,

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

可以在任何实例化之前检查模板的有效性。 [注意:知道哪些名称是类型名称,这样就可以检查每个模板的语法。 —结束说明]该程序格式错误,无需诊断,如果:

(8.1)-如果模板中的语句或constexpr的子语句未实例化,则无法为该模板或constexpr的子语句生成有效的专业化名称,或者

[..]

(8.4)-由于不依赖模板参数的构造,假设模板紧随其定义之后的假设实例化将不正确,或者

return C{B{}};不依赖模板,这是错误的。通过诊断问题可以解决clang问题。


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;
}
© www.soinside.com 2019 - 2024. All rights reserved.