使用 sfinae 检测可变参数模板的基数 类 是否具有特定方法
using sfinae to detect if base classes of a variadic template have specific method
我正在尝试 "iterate" 遍历可变参数派生 class 的所有基 classes 并调用一个名为 "stream" 的方法(如果存在)。
为了检查方法是否存在,我使用了 sfinae,它有效(参见注释掉的代码)。但是当我将它与可变参数模板组合时 "iteration" 如果不起作用,你的错误看起来有点像 sfinae 部分突然无法按预期工作,当在可变参数魔法中时。
感谢帮助。我使用 gcc 5.3.0.
#include <type_traits>
#include <iostream>
namespace detail{
template<class> struct sfinae_true : std::true_type{};
template<class T, class A0, class A1> static auto test_stream( int) -> sfinae_true<decltype(
std::declval<T>().stream(std::declval<A0>(), std::declval<A1>()))>;
template<class, class A0, class A1> static auto test_stream(long) -> std::false_type;
}
template<class T, class A0, class A1> struct has_stream : decltype(detail::test_stream<T, A0, A1>(0)){};
struct X{ void stream(int, bool){} };
struct A{ void stream(int, bool){} };
struct Y{};
template <typename ... T> class Z : public T ... {
public:
void ff() {
std::initializer_list<bool> {
( has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ...
};
}
};
int main(){
Z<X,A> a;
Z<X,A,Y> b;
/* this works as expected.
// this runs
if (has_stream<X, int, bool>()) {
std::cout << "has int, bool" << std::endl;
}
// and this doesn't
if (has_stream<Y, int, long>()) {
std::cout << "has int long" << std::endl;
}
*/
a.ff(); // no error
b.ff(); // error
}
$ g++ --std=c++14 -O0 2.cpp
2.cpp: In instantiation of ‘void Z<T>::ff() [with T = X, A, Y]’:
2.cpp:41:10: required from here
2.cpp:22:52: error: ‘stream’ is not a member of ‘Y’
( has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ...
^
2.cpp:21:9: error: no matching function for call to ‘std::initializer_list<bool>::initializer_list(<brace-enclosed initializer list>)’
std::initializer_list<bool> {
^
要么使用由你的类型特征组成的标签调度:
void ff()
{
std::initializer_list<int> {
(call<T>(has_stream<T,int,bool>{}), 0)...
};
}
template <typename U>
void call(std::true_type)
{
U::stream(0, 0);
}
template <typename U>
void call(std::false_type) {}
或使用表达式 SFINAE:
void ff()
{
std::initializer_list<int> {
(call<T>(0), 0)...
};
}
template <typename U>
auto call(int) -> decltype(U::stream(0, 0), void())
{
U::stream(0, 0);
}
template <typename U>
void call(char) {}
两个分支都应该在 ff
内有效。
您必须添加特定的方法来解决这个问题:
template <typename Base, typename T>
std::enable_if_t<has_stream<Base,int,bool>::value, bool>
call_stream_if_any(T& obj)
{
obj.Base::stream(0, 0);
return true;
}
template <typename Base, typename T>
std::enable_if_t<!has_stream<Base,int,bool>::value, bool>
call_stream_if_any(T& obj)
{
return false;
}
然后
template <typename ... T> class Z : public T ... {
public:
void ff() {
static_cast<void>(std::initializer_list<bool> {
( call_stream_if_any<T>(*this)) ...
});
}
};
这是带有一些私有辅助结构的 Z
class:
#include <iostream>
#include <type_traits>
namespace detail {
template <typename> struct TrueIfIsAType : std::true_type {};
template <typename T, typename... Args>
auto test (int) -> TrueIfIsAType< decltype(std::declval<T>().stream(std::declval<Args>()...)) >;
template <typename, typename...>
auto test (bool) -> std::false_type;
}
template <typename T, typename... Args>
struct HasStreamMember : decltype(detail::test<T, Args...>(0)) {};
template <typename... Ts>
struct Z : public Ts... {
void foo() {
const std::initializer_list<bool> a = {(HasStreamMember<Ts, int, bool>::value ? true : false)...};
const int b[] = {(Check<Ts>::execute(this, 0, 0), 0)...}; // The fix
std::cout << a.size() << '\n'; // Do whatever with 'a'.
static_cast<void>(b);
}
private:
template <typename T, typename = void>
struct Check {
static void execute (Z*, int, bool) {} // Do nothing.
};
template <typename T>
struct Check<T, std::enable_if_t<HasStreamMember<T, int, bool>::value>> {
static void execute (Z* z, int a, bool b) {
z->T::stream(a, b);
}
};
};
// Test
struct Thing { void stream(int, bool) {std::cout << "Thing stream\n";} };
struct Object { void stream(int, bool) {std::cout << "Object stream\n";} };
struct Widget {};
int main(){
std::cout << std::boolalpha << HasStreamMember<Thing, int, bool>() << '\n'; // true
std::cout << HasStreamMember<Object, int, bool>() << '\n'; // true
std::cout << HasStreamMember<Object, Widget>() << '\n'; // false
Z<Thing, Object> a;
Z<Thing, Object, Widget> b;
a.foo(); // Thing stream Object stream 2
b.foo(); // Thing stream Object stream 3
}
我正在尝试 "iterate" 遍历可变参数派生 class 的所有基 classes 并调用一个名为 "stream" 的方法(如果存在)。
为了检查方法是否存在,我使用了 sfinae,它有效(参见注释掉的代码)。但是当我将它与可变参数模板组合时 "iteration" 如果不起作用,你的错误看起来有点像 sfinae 部分突然无法按预期工作,当在可变参数魔法中时。
感谢帮助。我使用 gcc 5.3.0.
#include <type_traits>
#include <iostream>
namespace detail{
template<class> struct sfinae_true : std::true_type{};
template<class T, class A0, class A1> static auto test_stream( int) -> sfinae_true<decltype(
std::declval<T>().stream(std::declval<A0>(), std::declval<A1>()))>;
template<class, class A0, class A1> static auto test_stream(long) -> std::false_type;
}
template<class T, class A0, class A1> struct has_stream : decltype(detail::test_stream<T, A0, A1>(0)){};
struct X{ void stream(int, bool){} };
struct A{ void stream(int, bool){} };
struct Y{};
template <typename ... T> class Z : public T ... {
public:
void ff() {
std::initializer_list<bool> {
( has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ...
};
}
};
int main(){
Z<X,A> a;
Z<X,A,Y> b;
/* this works as expected.
// this runs
if (has_stream<X, int, bool>()) {
std::cout << "has int, bool" << std::endl;
}
// and this doesn't
if (has_stream<Y, int, long>()) {
std::cout << "has int long" << std::endl;
}
*/
a.ff(); // no error
b.ff(); // error
}
$ g++ --std=c++14 -O0 2.cpp
2.cpp: In instantiation of ‘void Z<T>::ff() [with T = X, A, Y]’:
2.cpp:41:10: required from here
2.cpp:22:52: error: ‘stream’ is not a member of ‘Y’
( has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ...
^
2.cpp:21:9: error: no matching function for call to ‘std::initializer_list<bool>::initializer_list(<brace-enclosed initializer list>)’
std::initializer_list<bool> {
^
要么使用由你的类型特征组成的标签调度:
void ff()
{
std::initializer_list<int> {
(call<T>(has_stream<T,int,bool>{}), 0)...
};
}
template <typename U>
void call(std::true_type)
{
U::stream(0, 0);
}
template <typename U>
void call(std::false_type) {}
或使用表达式 SFINAE:
void ff()
{
std::initializer_list<int> {
(call<T>(0), 0)...
};
}
template <typename U>
auto call(int) -> decltype(U::stream(0, 0), void())
{
U::stream(0, 0);
}
template <typename U>
void call(char) {}
两个分支都应该在 ff
内有效。
您必须添加特定的方法来解决这个问题:
template <typename Base, typename T>
std::enable_if_t<has_stream<Base,int,bool>::value, bool>
call_stream_if_any(T& obj)
{
obj.Base::stream(0, 0);
return true;
}
template <typename Base, typename T>
std::enable_if_t<!has_stream<Base,int,bool>::value, bool>
call_stream_if_any(T& obj)
{
return false;
}
然后
template <typename ... T> class Z : public T ... {
public:
void ff() {
static_cast<void>(std::initializer_list<bool> {
( call_stream_if_any<T>(*this)) ...
});
}
};
这是带有一些私有辅助结构的 Z
class:
#include <iostream>
#include <type_traits>
namespace detail {
template <typename> struct TrueIfIsAType : std::true_type {};
template <typename T, typename... Args>
auto test (int) -> TrueIfIsAType< decltype(std::declval<T>().stream(std::declval<Args>()...)) >;
template <typename, typename...>
auto test (bool) -> std::false_type;
}
template <typename T, typename... Args>
struct HasStreamMember : decltype(detail::test<T, Args...>(0)) {};
template <typename... Ts>
struct Z : public Ts... {
void foo() {
const std::initializer_list<bool> a = {(HasStreamMember<Ts, int, bool>::value ? true : false)...};
const int b[] = {(Check<Ts>::execute(this, 0, 0), 0)...}; // The fix
std::cout << a.size() << '\n'; // Do whatever with 'a'.
static_cast<void>(b);
}
private:
template <typename T, typename = void>
struct Check {
static void execute (Z*, int, bool) {} // Do nothing.
};
template <typename T>
struct Check<T, std::enable_if_t<HasStreamMember<T, int, bool>::value>> {
static void execute (Z* z, int a, bool b) {
z->T::stream(a, b);
}
};
};
// Test
struct Thing { void stream(int, bool) {std::cout << "Thing stream\n";} };
struct Object { void stream(int, bool) {std::cout << "Object stream\n";} };
struct Widget {};
int main(){
std::cout << std::boolalpha << HasStreamMember<Thing, int, bool>() << '\n'; // true
std::cout << HasStreamMember<Object, int, bool>() << '\n'; // true
std::cout << HasStreamMember<Object, Widget>() << '\n'; // false
Z<Thing, Object> a;
Z<Thing, Object, Widget> b;
a.foo(); // Thing stream Object stream 2
b.foo(); // Thing stream Object stream 3
}