通过元编程选择功能

时间:2014-10-03 07:48:16

标签: c++ templates c++11 template-meta-programming

假设用户可能提供或不提供以下两个功能:

void foo(int) { std::cout << "foo int" << std::endl; }
void foo() { std::cout << "foo void" << std::endl; }

在我的实现中,如果用户定义它,我想调用foo(int),否则调用foo()。这可以按如下方式完成:

template<class Int> auto call_foo(Int i) -> decltype(foo(i)) { return foo(i); }
template<class... Int> void call_foo(Int... i) { return foo(); } 

但是,如果我们想要做相反的事情,即更喜欢foo()而不是foo(int),则以下天真的尝试不起作用。

template<class Int> auto call_foo(Int i) -> decltype(foo()) { return foo(); }
template<class... Int> void call_foo(Int... i) { return foo(i...); } 

问题是decltype(foo())不依赖Int,因此foo()可能不存在不会导致SFINAE。

可能的解决方案是要求用户定义

中的任何一个
void foo(int, void*) { std::cout << "foo int" << std::endl; }
void foo(void*) { std::cout << "foo void" << std::endl; }

像这样,我们总是有foo的参数,我们可以在其中执行“模板与参数包”技巧。虽然这在技术上解决了这个问题,但它非常难看,因为它要求用户采取一个额外的参数,其含义对他/她来说可能并不明显。那么有没有办法实现相同的效果没有附加参数?

2 个答案:

答案 0 :(得分:4)

不需要SFINAE。只需标准的重载分辨率即可。

void call_func(void (*f)(), int) { f(); } // #1

template<class = void>
void call_func(void (*f)(int), int x) { f(x); } // #2

call_func(foo, 1); // #1 is preferred if viable. All others being equal,
                   // non-templates are preferred over templates

答案 1 :(得分:3)

以下可能会有所帮助:

template<typename ... Ts> void foo(Ts...) = delete;
void foo(int) { std::cout << "foo int" << std::endl; }
//void foo() { std::cout << "foo void" << std::endl; }

namespace detail
{

struct overload_priority_low {};
struct overload_priority_high : overload_priority_low{};

template <typename Int>
auto call_foo(Int i, overload_priority_low) -> decltype(foo(i)) { return foo(i); }

template <typename Int>
auto call_foo(Int , overload_priority_high) -> decltype(sizeof(Int), foo()) { return foo(); }

}

void call_foo(int i)
{
    return detail::call_foo(i, detail::overload_priority_high{});
}

Live example