evn*_*vnu 144 c++ templates g++ c++11
我想尝试一个简单的例子来了解如何使用std::enable_if.在我读完这个答案之后,我认为想出一个简单的例子应该不会太难.我想用来std::enable_if在两个成员函数之间进行选择,并且只允许使用其中一个成员函数.
不幸的是,下面的代码不能用gcc 4.7进行编译,经过数小时和数小时的尝试后我会问你们我的错误是什么.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
gcc报告以下问题:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Run Code Online (Sandbox Code Playgroud)
为什么g ++没有为第二个成员函数删除错误的实例化?根据标准,std::enable_if< bool, T = void >::type仅在布尔模板参数为true时存在.但是为什么g ++不认为这是SFINAE?我认为重载错误消息来自g ++不删除第二个成员函数的问题,并认为这应该是一个重载.
Joh*_*itb 110
SFINAE仅在模板参数的参数推断中的替换使得构造不正确时才起作用.没有这样的替代品.
我也想到了这一点,并尝试使用
std::is_same< T, int >::value,! std::is_same< T, int >::value并给出相同的结果.
这是因为当实例化类模板时(Y<int>在其他情况下创建类型的对象时会发生这种情况),它会实例化其所有成员声明(不一定是它们的定义/主体!).其中还包括其成员模板.注意,这T是已知的,并!std::is_same< T, int >::value产生错误.所以它将创建一个Y<int>包含的类
class Y<int> {
public:
/* instantiated from
template < typename = typename std::enable_if<
std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/
template < typename = typename std::enable_if< true >::type >
int foo();
/* instantiated from
template < typename = typename std::enable_if<
! std::is_same< T, int >::value >::type >
T foo() {
return 10;
}
*/
template < typename = typename std::enable_if< false >::type >
int foo();
};
Run Code Online (Sandbox Code Playgroud)
的std::enable_if<false>::type访问不存在的类型,所以是形成不良的该声明.因此您的程序无效.
您需要使成员模板enable_if依赖于成员模板本身的参数.然后声明是有效的,因为整个类型仍然是依赖的.当您尝试调用其中一个时,会发生模板参数的参数推断,并且SFINAE会按预期发生.请参阅此问题以及有关如何执行此操作的相应答案.
jpi*_*ihl 82
我做了这个简短的例子,也有效.
#include <iostream>
#include <type_traits>
class foo;
class bar;
template<class T>
struct is_bar
{
template<class Q = T>
typename std::enable_if<std::is_same<Q, bar>::value, bool>::type check()
{
return true;
}
template<class Q = T>
typename std::enable_if<!std::is_same<Q, bar>::value, bool>::type check()
{
return false;
}
};
int main()
{
is_bar<foo> foo_is_bar;
is_bar<bar> bar_is_bar;
if (!foo_is_bar.check() && bar_is_bar.check())
std::cout << "It works!" << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
评论如果你想让我详细说明.我认为代码或多或少是不言自明的,但后来我又做了所以我可能错了:)
你可以在这里看到它.
use*_*631 13
对于那些寻找"正常工作"解决方案的后来者:
#include <utility>
#include <iostream>
template< typename T >
class Y {
template< bool cond, typename U >
using resolvedType = typename std::enable_if< cond, U >::type;
public:
template< typename U = T >
resolvedType< true, U > foo() {
return 11;
}
template< typename U = T >
resolvedType< false, U > foo() {
return 12;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
编译:
g++ -std=gnu++14 test.cpp
Run Code Online (Sandbox Code Playgroud)
跑步给出:
./a.out
11
Run Code Online (Sandbox Code Playgroud)
从这篇文章:
默认模板参数不是模板签名的一部分
但是人们可以这样做:
#include <iostream>
struct Foo {
template < class T,
class std::enable_if < !std::is_integral<T>::value, int >::type = 0 >
void f(const T& value)
{
std::cout << "Not int" << std::endl;
}
template<class T,
class std::enable_if<std::is_integral<T>::value, int>::type = 0>
void f(const T& value)
{
std::cout << "Int" << std::endl;
}
};
int main()
{
Foo foo;
foo.f(1);
foo.f(1.1);
// Output:
// Int
// Not int
}
Run Code Online (Sandbox Code Playgroud)
小智 5
解决这个问题的一种方法是,成员函数的特化是将特化放入另一个类,然后从该类继承.您可能必须更改继承的顺序才能访问所有其他基础数据,但此技术确实有效.
template< class T, bool condition> struct FooImpl;
template<class T> struct FooImpl<T, true> {
T foo() { return 10; }
};
template<class T> struct FoolImpl<T,false> {
T foo() { return 5; }
};
template< class T >
class Y : public FooImpl<T, boost::is_integer<T> > // whatever your test is goes here.
{
public:
typedef FooImpl<T, boost::is_integer<T> > inherited;
// you will need to use "inherited::" if you want to name any of the
// members of those inherited classes.
};
Run Code Online (Sandbox Code Playgroud)
这种技术的缺点是,如果你需要为不同的成员函数测试很多不同的东西,你必须为每个成员函数创建一个类,并在继承树中链接它.这对于访问公共数据成员是正确的.
例如:
template<class T, bool condition> class Goo;
// repeat pattern above.
template<class T, bool condition>
class Foo<T, true> : public Goo<T, boost::test<T> > {
public:
typedef Goo<T, boost::test<T> > inherited:
// etc. etc.
};
Run Code Online (Sandbox Code Playgroud)
布尔值需要依赖于被推导的模板参数。因此,一个简单的修复方法是使用默认的布尔参数:
template< class T >
class Y {
public:
template < bool EnableBool = true, typename = typename std::enable_if<( std::is_same<T, double>::value && EnableBool )>::type >
T foo() {
return 10;
}
};
Run Code Online (Sandbox Code Playgroud)
但是,如果您想重载成员函数,这将不起作用。相反,最好使用TickTICK_MEMBER_REQUIRES库:
template< class T >
class Y {
public:
TICK_MEMBER_REQUIRES(std::is_same<T, double>::value)
T foo() {
return 10;
}
TICK_MEMBER_REQUIRES(!std::is_same<T, double>::value)
T foo() {
return 10;
}
};
Run Code Online (Sandbox Code Playgroud)
您还可以实现自己的成员需要像这样的宏(以防万一您不想使用其他库):
template<long N>
struct requires_enum
{
enum class type
{
none,
all
};
};
#define MEMBER_REQUIRES(...) \
typename requires_enum<__LINE__>::type PrivateRequiresEnum ## __LINE__ = requires_enum<__LINE__>::type::none, \
class=typename std::enable_if<((PrivateRequiresEnum ## __LINE__ == requires_enum<__LINE__>::type::none) && (__VA_ARGS__))>::type
Run Code Online (Sandbox Code Playgroud)
问题是,这两个声明foo()除了默认模板参数之外没有任何区别,并且这并不会使它们成为单独的声明。定义第二个foo()只是重新定义第一个foo()。
以下是选项摘要,从最现代到最不现代:
requires使用子句条件编译成员函数c++20T foo() requires some_condition<T> {
return 10;
}
// Trailing requires clause could also be omitted here because the first
// function is more constrained, and overload resolution will prefer it.
T foo() requires (!some_condition<T>) {
return 5;
}
Run Code Online (Sandbox Code Playgroud)
// This can only be used to select an implementation, not to make foo()
// disappear entirely.
T foo() {
if constexpr (some_condition<T>) {
// If this part more complex, you can put it in a private member function.
return 10;
}
else {
return 5;
}
}
Run Code Online (Sandbox Code Playgroud)
std::enable_if c++11// note: Unfortunately, we pollute the function signature with additional template
// parameters.
// note: std::enable_if_t can be used in C++14
// note: The additional U parameter is necessary to make the condition dependent
// on a template parameter; otherwise the compiler will simply emit an error
// for some_condition<T>.
template <typename U = T, typename std::enable_if<some_condition<U>, int>::type = 0>
T foo() {
return 10;
}
template <typename U = T, typename std::enable_if<!some_condition<U>, int>::type = 0>
T foo() {
return 5;
}
Run Code Online (Sandbox Code Playgroud)
std::enable_if // do SFINAE in trailing return type
template <typename U = T>
auto foo()
-> typename std::enable_if<some_condition<U>, T>::type {
return 10;
}
// do SFINAE in regular return type
template <typename U = T>
typename std::enable_if<!some_condition<U>, T>::type
foo() {
return 5;
}
Run Code Online (Sandbox Code Playgroud)
template <typename T, bool B>
struct mixin;
template <typename T>
struct mixin<T, true> {
T foo() { return 10; }
};
template <typename T>
struct mixin<T, false> {
T foo() { return 5; }
};
template <typename T>
struct Y : mixin<T, some_condition<T>> {};
Run Code Online (Sandbox Code Playgroud)