具有默认类型的C ++函数引用模板替换错误

时间:2016-10-12 17:06:22

标签: c++ c++11 language-lawyer

请考虑以下代码段:

#include <type_traits>
#include <string>

template<typename T>
std::string stringify (const T& value)
{ return "dummy"; }

template<typename T>
class foo_class
{
public:
    template<typename Converter = std::string(&)(const T&),
            class = typename std::enable_if<std::is_convertible<
                                                    typename std::result_of<Converter(const T&)>::type,
                                                    std::string>
                                            ::value>::type>
    foo_class(const T &value, const Converter &converter = stringify<T>) {}
};

int main(int,const char*[])
{
    int X = 7;
    foo_class<int> x(X);
    foo_class<int> y(X, stringify<int>);
}

第一个构造函数编译得很好,但是第二个构造函数在Clang 3.6.2下失败并出现以下错误:

candidate template ignored: substitution failure [with Converter = std::__cxx11::basic_string<char> (const int &)]: function cannot return function type 'std::__cxx11::basic_string<char> (const int &)'
    foo_class(const T &value, const Converter &converter = stringify<T>) {}

我通过在参数列表中将const Converter &更改为Converter来找到一种方法来修复它,尽管在某些情况下这可能不是理想的行为。

错误是由std :: enable_if&lt; ..&gt;中的某些内容引起的条款。我可以删除它,代码编译(并运行)就好了。

我主要对这个问题感兴趣&#34;为什么&#34; - 为什么它在第一种情况下工作(当选择函数作为默认参数时),但在第二种情况下,当显式选择它时不工作。

作为第二个问题,什么被认为是解决问题的最佳方式?我的意思是,我有一个解决方法,但更喜欢坚持使用&#34; const引用默认情况下#34;参数的政策不是函数。

2 个答案:

答案 0 :(得分:1)

这不是你应该如何使用result_of

它应该始终与引用一起使用,其中引用的类型指定要知道其结果的调用中相应表达式的值类别。

因此,如果您将converter称为const左值,那么您需要result_of<const Converter &(const T&)>。还有一些例子:

// forward the value category of the function object
template<class F> 
auto f(F&& f) -> std::result_of_t<F&&()> { return std::forward<F>(f)(); }

// always call as an lvalue, accept both lvalue and rvalue function object
template<class F> 
auto f(F&& f) -> std::result_of_t<F&()> { return f(); }

// Accept everything by value, but call with all lvalues
template<class F, class... Args> 
auto f(F f, Args... args) -> std::result_of_t<F&(Args&...)> { return f(args...); }

// Accept everything by value, and move them all
template<class F, class... Args> 
auto f(F f, Args... args) -> std::result_of_t<F&&(Args&&...)> { return std::move(f)(std::move(args)...); }

答案 1 :(得分:0)

在你的第二个片段中,你有

Converter = std::string(const T&) // without reference

2种可能的修复/解决方法:

使用std::decay_t

template<typename T>
class foo_class
{
public:
    template<typename Converter = std::string(&)(const T&),
             std::enable_if_t<
                 std::is_convertible<
                     std::result_of_t<std::decay_t<Converter>(const T&)>,
                     std::string>::value>* = nullptr>
    foo_class(const T &value, const Converter &converter = stringify<T>) {}
};

Demo

或使用

foo_class<int> y(X, &stringfy<int>);