提问人:evnu 提问时间:8/7/2011 最后编辑:Jan Schultkeevnu 更新时间:9/14/2023 访问量:182498
std::enable_if 有条件地编译成员函数
std::enable_if to conditionally compile a member function
问:
我正在尝试获得一个简单的示例来了解如何使用 .看完这个答案后,我觉得想出一个简单的例子应该不会太难。我想用于在两个成员函数之间进行选择,并且只允许使用其中一个。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++ 不删除第二个成员函数的错误实例化?根据标准,仅当布尔模板参数为 true 时才存在。但是为什么 g++ 不认为这是 SFINAE?我认为重载错误消息来自g++不删除第二个成员函数的问题,并认为这应该是重载。std::enable_if< bool, T = void >::type
答:
仅当模板参数的参数推导中的替换使构造格式不正确时,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();
};
访问不存在的类型,因此声明的格式不正确。因此,您的程序是无效的。std::enable_if<false>::type
您需要使成员模板依赖于成员模板本身的参数。然后声明是有效的,因为整个类型仍然是依赖的。当您尝试调用其中一个时,将对其模板参数进行参数推导,并且 SFINAE 按预期发生。请参阅此问题以及有关如何执行此操作的相应答案。enable_if
评论
Y
T
解决这个问题的一种方法,即成员函数的专用化是将专用化放入另一个类中,然后从该类继承。您可能需要更改继承顺序才能访问所有其他基础数据,但此技术确实有效。
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.
};
这种技术的缺点是,如果你需要为不同的成员函数测试很多不同的东西,你必须为每个函数创建一个类,并将其链接到继承树中。对于访问公共数据成员也是如此。
前任:
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.
};
我做了这个简短的例子,它也有效。
#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;
}
如果你想让我详细说明,请发表评论。我认为代码或多或少是不言自明的,但话又说回来,我做到了,所以我可能错了:)
你可以在这里看到它的实际效果。
评论
error C4519: default template arguments are only allowed on a class template
Q
T
Q*
Q*
(Q*)NULL
布尔值需要依赖于正在推导的模板参数。因此,一个简单的修复方法是使用默认的布尔参数:
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;
}
};
但是,如果要使成员函数重载,则此操作将不起作用。相反,最好使用 Tick 库中的TICK_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;
}
};
你也可以像这样实现你自己的成员需要宏(以防万一你不想使用其他库):
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
评论
从这篇文章:
默认模板参数不是模板签名的一部分
但是可以做这样的事情:
#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
}
评论
typename
class
对于那些正在寻找“行之有效”解决方案的后来者:
#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;
}
编译方式:
g++ -std=gnu++14 test.cpp
跑步可以带来:
./a.out
11
评论
std::enable_if_t
resolvedType
这是我的极简主义示例,使用宏。
使用更复杂的表达式时,请使用双括号。enable_if((...))
template<bool b, std::enable_if_t<b, int> = 0>
using helper_enable_if = int;
#define enable_if(value) typename = helper_enable_if<value>
struct Test
{
template<enable_if(false)>
void run();
}
评论
template <typename = std::enable_if_t<b, int> = 0>
template <bool b, std::enable_if_t<b, int> = 0>
// Try this one:
#include <iostream>
#include <type_traits>
// suppose you want to disable certain member functions based on the tag
struct FooTag;
struct BarTag;
// macro to save some typings in the following
// note that a dummy typename is involved in both the
// first and second parameters.
// this should be different than the template parameter of the class (typename T for Widget below)
#define EnableIfFoo(T) \
template <typename Dummy = void, typename = \
typename std::enable_if<std::is_same<FooTag, T>::value, Dummy>::type>
#define EnableIfBar(T) \
template <typename Dummy = void, typename = \
typename std::enable_if<std::is_same<BarTag, T>::value, Dummy>::type>
template <typename T>
class Widget {
public:
// enable this function only if the tag is Bar
EnableIfFoo(T)
void print() const { std::cout << "I am a Foo!" << std::endl; }
// enable this function only if the tag is Foo
EnableIfBar(T)
void display() const { std::cout << "I am a Bar!" << std::endl; }
};
int main() {
// instantiate a widget with tag Foo
// only print is enabled; display is not
Widget<FooTag> fw;
fw.print();
//fw.display(); // compile error !!
// instantiate a Widget using tag Bar
// only display is enabled; print is not
Widget<BarTag> bw;
bw.display();
//bw.print(); // compile error !!
return 0;
}
评论
问题在于,除了默认模板参数之外,这两个声明没有任何不同之处,这并不能使它们成为单独的声明。
定义第二个只是重新定义第一个。foo()
foo()
foo()
以下是从最现代到最不现代的选项摘要:
有条件地编译子句 c++20 的成员函数requires
T foo() requires some_condition<T> {
return 10;
}
// note: Trailing requires clause could also be omitted here
// because the first function is more constrained, and overload resolution
// will select the better one.
T foo() requires (!some_condition<T>) {
return 5;
}
解决办法:选择具有 if constexpr c++17 的实现
T foo() {
if constexpr (some_condition<T>) {
// if this is more complex, you can put it in a private member function
return 10;
}
else {
return 5;
}
}
正确使用 c++11std::enable_if
// 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;
}
Mixins c++98
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>> {};
上一个:模板模板参数有哪些用途?
评论
std::is_same< T, int >::value
! std::is_same< T, int >::value