提供与 TMP 和 SFINAE 的通用接口
Providing generic interface with TMP and SFINAE
目前,我有以下使用 class X
的工作代码,我为多个 classes 提供了一个通用接口 - 我只期望静态 f
函数存在,但我既不修复 return 类型也不修复参数:
#include <iostream>
#include <type_traits>
class A {
public:
static void f()
{
std::cout << "A::f()" << std::endl;
}
};
class B {
public:
static size_t f(std::string const& s_)
{
std::cout << "B::f()" << std::endl;
return s_.size();
}
};
class C {
};
template <typename T>
class X {
public:
static
void
f(...)
{
std::cout << "Operation not supported!" << std::endl;
}
template <typename... Args>
static
std::result_of_t<decltype(&T::f)(Args...)>
f(Args&&... args_)
{
return T::f(std::forward<Args>(args_)...);
}
};
int main()
{
X<A>::f(); // Compiles, but unexpected overload!
auto const x = X<B>::f("Hello"); // Works just fine
std::cout << x << std::endl;
// X<C>::f(); // Does NOT compile!
return 0;
}
但是,我遇到了多个问题(如上面评论中所标记):
如果我允许该函数不存在并取消注释该行
使用 C
作为模板参数,代码无法编译,它
仍然期望 C
有一个函数 f
:
In instantiation of ‘class X<C>’:
required from here
error: ‘f’ is not a member of ‘C’
std::result_of_t<decltype(&T::f)(Args...)>
基于 this,我希望省略号参数可以解决问题。
另一方面,即使这行得通,我还会遇到另一个问题:虽然 A
提供了 f
,但重载
省略号参数是在重载解析期间选择的 - 当然,其中 A
提供的 f
是首选。
(使用的编译器:g++ (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0;使用的标准:C++14。)
欢迎任何帮助解决上述问题(最好同时解决两个问题)。
我提出以下X
结构
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
积分是
(1) 通过一个带有额外未使用参数的g()
函数(可变参数版本中的int
,被"not supported version"中的...
吸附),允许select 可变参数版本(使用 0
调用,即 int
),当 sizeof...(As) == 0
和两个 g()
函数可用时。这是因为 int
对于 int
比 ...
更匹配(完全匹配)。但是当可变版本不可用时(见第(2)点),"non supported" 版本仍然可用(唯一可用的)和 selected
(2) 根据 f()
静态方法在 T
中可用(或不可用)这一事实,您必须使用 SFINAE enable/disable 可变参数版本。不幸的是 T
是 class 的模板参数,而不是 g()
静态方法的模板参数,因此您可以将它用于 SFINAE。诀窍是 SFINAE 操作一个额外的模板参数,U
,默认为 T
// ------------------------VVVVVVVVVVVVVV additional U (defaulted to T) template type
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
// -------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// SFINAE enable this g() if (and only if) the call
// U::f(std::forward<As>(as)...) is valid
以下是一个简化的工作示例
#include <iostream>
#include <type_traits>
struct A
{ static void f() { std::cout << "A::f()" << std::endl; } };
struct B
{
static size_t f(std::string const& s_)
{ std::cout << "B::f()" << std::endl; return s_.size(); }
};
struct C
{ };
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
int main ()
{
X<A>::f(); // now call variadic version
auto const x = X<B>::f("Hello"); // Works just fine as before
std::cout << x << std::endl;
X<C>::f(); // now compile
}
目前,我有以下使用 class X
的工作代码,我为多个 classes 提供了一个通用接口 - 我只期望静态 f
函数存在,但我既不修复 return 类型也不修复参数:
#include <iostream>
#include <type_traits>
class A {
public:
static void f()
{
std::cout << "A::f()" << std::endl;
}
};
class B {
public:
static size_t f(std::string const& s_)
{
std::cout << "B::f()" << std::endl;
return s_.size();
}
};
class C {
};
template <typename T>
class X {
public:
static
void
f(...)
{
std::cout << "Operation not supported!" << std::endl;
}
template <typename... Args>
static
std::result_of_t<decltype(&T::f)(Args...)>
f(Args&&... args_)
{
return T::f(std::forward<Args>(args_)...);
}
};
int main()
{
X<A>::f(); // Compiles, but unexpected overload!
auto const x = X<B>::f("Hello"); // Works just fine
std::cout << x << std::endl;
// X<C>::f(); // Does NOT compile!
return 0;
}
但是,我遇到了多个问题(如上面评论中所标记):
如果我允许该函数不存在并取消注释该行 使用
C
作为模板参数,代码无法编译,它 仍然期望C
有一个函数f
:In instantiation of ‘class X<C>’: required from here error: ‘f’ is not a member of ‘C’ std::result_of_t<decltype(&T::f)(Args...)>
基于 this,我希望省略号参数可以解决问题。
另一方面,即使这行得通,我还会遇到另一个问题:虽然
A
提供了f
,但重载 省略号参数是在重载解析期间选择的 - 当然,其中A
提供的f
是首选。
(使用的编译器:g++ (Ubuntu 8.1.0-5ubuntu1~16.04) 8.1.0;使用的标准:C++14。)
欢迎任何帮助解决上述问题(最好同时解决两个问题)。
我提出以下X
结构
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
积分是
(1) 通过一个带有额外未使用参数的g()
函数(可变参数版本中的int
,被"not supported version"中的...
吸附),允许select 可变参数版本(使用 0
调用,即 int
),当 sizeof...(As) == 0
和两个 g()
函数可用时。这是因为 int
对于 int
比 ...
更匹配(完全匹配)。但是当可变版本不可用时(见第(2)点),"non supported" 版本仍然可用(唯一可用的)和 selected
(2) 根据 f()
静态方法在 T
中可用(或不可用)这一事实,您必须使用 SFINAE enable/disable 可变参数版本。不幸的是 T
是 class 的模板参数,而不是 g()
静态方法的模板参数,因此您可以将它用于 SFINAE。诀窍是 SFINAE 操作一个额外的模板参数,U
,默认为 T
// ------------------------VVVVVVVVVVVVVV additional U (defaulted to T) template type
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
// -------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// SFINAE enable this g() if (and only if) the call
// U::f(std::forward<As>(as)...) is valid
以下是一个简化的工作示例
#include <iostream>
#include <type_traits>
struct A
{ static void f() { std::cout << "A::f()" << std::endl; } };
struct B
{
static size_t f(std::string const& s_)
{ std::cout << "B::f()" << std::endl; return s_.size(); }
};
struct C
{ };
template <typename T>
struct X
{
static void g (...)
{ std::cout << "Operation not supported!" << std::endl; }
template <typename ... As, typename U = T>
static auto g(int, As && ... as)
-> decltype( U::f(std::forward<As>(as)...) )
{ return T::f(std::forward<As>(as)...); }
template <typename ... As>
static auto f (As && ... as)
{ return g(0, std::forward<As>(as)...); }
};
int main ()
{
X<A>::f(); // now call variadic version
auto const x = X<B>::f("Hello"); // Works just fine as before
std::cout << x << std::endl;
X<C>::f(); // now compile
}