std::enable_if 有条件地编译成员函数
我试图通过一个简单的示例来理解如何使用 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;
}
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()'
为什么 g++ 不删除第二个成员函数的错误实例化?根据标准,std::enable_if< bool, T = void >::type
仅当布尔模板参数为 true 时才存在。但为什么 g++ 不认为这是 SFINAE 呢?我认为重载错误消息来自于g++没有删除第二个成员函数并认为这应该是重载的问题。
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.
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.
#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:
% 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
仅当模板参数的参数推导中的替换导致构造格式错误时,SFINAE 才起作用。不存在这样的替代。
这是因为当实例化类模板时(在其他情况下创建
Y
类型的对象时会发生这种情况),它会实例化其所有成员声明(不一定是它们的定义/主体!)。其中还有其成员模板。请注意,此时T
是已知的,并且!std::is_same
!std::is_same
产生 false。因此它将创建一个类T
是已知的。 T, int >::valueY
,其中包含std::enable_if::type
访问不存在的类型,因此该声明是错误的 -形成。因此你的程序是无效的。您需要使成员模板的
enable_if
依赖于成员模板本身的参数。那么声明是有效的,因为整个类型仍然是依赖的。当您尝试调用其中之一时,会发生模板参数的参数推导,并且 SFINAE 会按预期发生。请参阅这个问题以及有关如何做到这一点的相应答案。SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. There is no such substitution.
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 thatT
is known then, and!std::is_same< T, int >::value
yields false. So it will create a classY<int>
which containsThe
std::enable_if<false>::type
accesses a non-existing type, so that declaration is ill-formed. And thus your program is invalid.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 question and the corresponding answer on how to do that.我做了这个简短的例子,它也有效。
如果您想让我详细说明,请评论。我认为代码或多或少是不言自明的,但我再次制作了它,所以我可能是错的:)
你可以看到它的实际效果
I made this short example which also works.
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.
对于那些正在寻找“有效”解决方案的后来者:
编译:
运行给出:
For those late-comers that are looking for a solution that "just works":
Compile with:
Running gives:
来自这篇帖子:
,但可以执行以下操作:
From this post:
But one can do something like this:
问题是
foo()
的两个声明除了默认模板参数之外没有任何区别,并且这并不会使它们成为单独的声明。定义第二个
foo()
只是重新定义第一个foo()
。以下是从最现代到最不现代的选项摘要:
使用
requires
子句有条件地编译成员函数 c++20解决方法:使用 if constexpr c++17
正确使用
std::enable_if
c++11std::enable_if
的替代样式 c++11混合 c++98
The issue is that both declarations of
foo()
differ in nothing but the default template arguments, and that doesn't make them separate declarations.Defining the second
foo()
is just re-defining the firstfoo()
.Here is a summary of options, from most modern to least modern:
Conditionally compile member function with
requires
clause c++20Workaround: select implementation with if constexpr c++17
Proper use of
std::enable_if
c++11Alternative style for
std::enable_if
c++11Mixins c++98
解决这个问题的一种方法是,成员函数的特化是将特化放入另一个类中,然后从该类继承。您可能必须更改继承顺序才能访问所有其他基础数据,但此技术确实有效。
这种技术的缺点是,如果您需要为不同的成员函数测试许多不同的东西,则必须为每个成员函数创建一个类,并将其链接到继承树中。对于访问公共数据成员来说也是如此。
前任:
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.
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:
布尔值需要依赖于被推导的模板参数。因此,一个简单的解决方法是使用默认的布尔参数:
但是,如果您想重载成员函数,这将不起作用。相反,最好使用
TICK_MEMBER_REQUIRES
来自 Tick 库:您还可以实现自己的成员需要像这样的宏(只需在如果您不想使用另一个库):
The boolean needs to depend on the template parameter being deduced. So an easy way to fix is to use a default boolean parameter:
However, this won't work if you want to overload the member function. Instead, its best to use
TICK_MEMBER_REQUIRES
from the Tick library:You can also implement your own member requires macro like this(just in case you don't want to use another library):
这是我使用宏的极简示例。
使用更复杂的表达式时,请使用双括号
enable_if((...))
。Here is my minimalist example, using a macro.
Use double brackets
enable_if((...))
when using more complex expressions.