enable_if g++ 和 clang++ 之间的区别
Difference between g++ and clang++ with enable_if
我想编写一个 returns 类型 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++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;
}
两个编译器都对,
http://eel.is/c++draft/temp.res#8.1
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
(8.1)
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or
[..]
(8.4)
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
return C{B{}};
不依赖于模板,而且是错误的。通过诊断问题,clang 很好。
我想编写一个 returns 类型 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++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;
}
两个编译器都对,
http://eel.is/c++draft/temp.res#8.1
The validity of a template may be checked prior to any instantiation. [ Note: Knowing which names are type names allows the syntax of every template to be checked in this way. — end note ] The program is ill-formed, no diagnostic required, if:
(8.1)
- no valid specialization can be generated for a template or a substatement of a constexpr if statement within a template and the template is not instantiated, or[..]
(8.4)
- a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or
return C{B{}};
不依赖于模板,而且是错误的。通过诊断问题,clang 很好。