C++ std::enable_if 有条件地编译成员函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6972368/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
std::enable_if to conditionally compile a member function
提问by evnu
I am trying to get a simple example to work to understand how to use std::enable_if
. After I read this answer, I thought it shouldn't be too hard to come up with a simple example. I want to use std::enable_if
to choose between two member-functions and allow only one of them to be used.
我正在尝试通过一个简单的示例来了解如何使用std::enable_if
. 在我阅读了这个答案之后,我认为想出一个简单的例子应该不会太难。我想std::enable_if
在两个成员函数之间进行选择,并且只允许使用其中一个。
Unfortunately, the following doesn't compile with gcc 4.7 and after hours and hours of trying I am asking you guys what my mistake is.
不幸的是,以下内容不能用 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;
}
gcc reports the following problems:
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()'
Why doesn't g++ delete the wrong instantiation for the second member function? According to the standard, std::enable_if< bool, T = void >::type
only exists when the boolean template parameter is true. But why doesn't g++ consider this as SFINAE? I think that the overloading error message comes from the problem that g++ doesn't delete the second member function and believes that this should be an overload.
为什么 g++ 不删除第二个成员函数的错误实例化?根据标准,std::enable_if< bool, T = void >::type
仅当布尔模板参数为真时才存在。但是为什么 g++ 不认为这是 SFINAE 呢?我认为重载错误信息来自g++没有删除第二个成员函数并认为这应该是重载的问题。
采纳答案by Johannes Schaub - litb
SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. There is no such substitution.
SFINAE 仅在模板参数的参数推导中的替换使构造格式错误时才有效。没有这样的替代。
I thought of that too and tried to use
std::is_same< T, int >::value
and! std::is_same< T, int >::value
which gives the same result.
我想的太并试图用
std::is_same< T, int >::value
和! std::is_same< T, int >::value
可以得到相同的结果。
That's because when the class template is instantiated (which happens when you create an object of type Y<int>
among other cases), it instantiates all its member declarations (not necessarily their definitions/bodies!). Among them are also its member templates. Note that T
is known then, and !std::is_same< T, int >::value
yields false. So it will create a class Y<int>
which contains
那是因为当类模板被实例化时(当你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();
};
The std::enable_if<false>::type
accesses a non-existing type, so that declaration is ill-formed. And thus your program is invalid.
的std::enable_if<false>::type
访问不存在的类型,所以是形成不良的该声明。因此您的程序无效。
You need to make the member templates' enable_if
depend on a parameter of the member template itself. Then the declarations are valid, because the whole type is still dependent. When you try to call one of them, argument deduction for their template arguments happen and SFINAE happens as expected. See this questionand the corresponding answer on how to do that.
您需要使成员模板enable_if
依赖于成员模板本身的参数。那么声明是有效的,因为整个类型仍然是依赖的。当您尝试调用其中之一时,它们的模板参数的参数推导会发生,并且 SFINAE 会按预期发生。请参阅此问题以及有关如何执行此操作的相应答案。
回答by jpihl
I made this short example which also works.
我做了这个简短的例子,它也有效。
#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;
}
Comment if you want me to elaborate. I think the code is more or less self-explanatory, but then again I made it so I might be wrong :)
如果你想让我详细说明,请评论。我认为代码或多或少是不言自明的,但是我又做了一遍,所以我可能是错的:)
You can see it in action here.
你可以在这里看到它的实际效果。
回答by user1284631
For those late-comers that are looking for a solution that "just works":
对于那些正在寻找“有效”解决方案的后来者:
#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;
}
Compile with:
编译:
g++ -std=gnu++14 test.cpp
Running gives:
跑步给出:
./a.out
11
回答by Janek Olszak
From thispost:
从这篇文章:
Default template arguments are not part of the signature of a template
默认模板参数不是模板签名的一部分
But one can do something like this:
但是可以做这样的事情:
#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
}
回答by Gary Powell
One way to solve this problem, specialization of member functions is to put the specialization into another class, then inherit from that class. You may have to change the order of inheritence to get access to all of the other underlying data but this technique does work.
解决这个问题的一种方法,成员函数的特化是把特化放到另一个类中,然后从那个类继承。您可能必须更改继承顺序才能访问所有其他底层数据,但这种技术确实有效。
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.
};
The disadvantage of this technique is that if you need to test a lot of different things for different member functions you'll have to make a class for each one, and chain it in the inheritence tree. This is true for accessing common data members.
这种技术的缺点是如果你需要为不同的成员函数测试很多不同的东西,你必须为每个函数创建一个类,并将它链接到继承树中。这适用于访问公共数据成员。
Ex:
前任:
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.
};
回答by Paul Fultz II
The boolean needs to depend on the template parameter being deduced. So an easy way to fix is to use a default boolean parameter:
布尔值需要依赖于推导的模板参数。所以一个简单的修复方法是使用默认的布尔参数:
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;
}
};
However, this won't work if you want to overload the member function. Instead, its best to use TICK_MEMBER_REQUIRES
from the Ticklibrary:
但是,如果您想重载成员函数,这将不起作用。相反,最好TICK_MEMBER_REQUIRES
从Tick库中使用:
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;
}
};
You can also implement your own member requires macro like this(just in case you don't want to use another library):
您也可以像这样实现自己的成员 requires 宏(以防万一您不想使用其他库):
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