使用std :: function作为模板参数+非模板函数重载

时间:2015-04-11 10:12:53

标签: templates c++11 overloading

这是问题所在。论坛主题底部的代码只是我认为在仅基于参数类型的重载函数推导的情况下再现的编译器错误的最简单示例。 有一个功能:

void f(const T& v, const std::function< bool( const T& ) >& validator );

...需要一些价值和验证者。因此,由于不需要验证bool,因此有一个无验证器的函数重载版本:

void f(const bool& v);

最确切地说,应该禁用函数模板生成一个以bool为值的函数:

template<bool>
void f(const bool& v, const std::function< bool(const bool&) >& validator) = delete;

...但这不是为了简化代码而制作的。

如果使用包含在MSVS2013中的MSVC12编译代码,则主题底部的代码将无法编译。两行:

f(5, [](const int& a){ return a != 0; });
f(5, & isIntValid );

...会发出C2660错误,告诉&#34; f&#34;不带2个参数,或者没有这样的重载版本可以接受那么多的参数。当然,这是一个完全谎言和编译器错误。 现在,评论一下:

#define USE_FUNCTOR

...以便启用&#34; f&#34;的版本使用函数指针参数作为验证器而不是函子。然后代码编译得非常好。如果你喜欢用普通bool替换函数指针参数:

template< typename T >
void f(const T& v, const bool isValid )
{
    if (isValid)
    {
        std::cout << v;
    }
}

...然后调用函数:

f( 5, false );

再次出现任何编译错误。 显式修复:通过显式指定模板参数类型来调用f的模板版本: 这些行:

f(5, [](const int& a){ return a != 0; });
f(5, & isIntValid );

......应该成为:

f<int>(5, [](const int& a){ return a != 0; });
f<int>(5, & isIntValid );

但是,显而易见的是,这种差异化的使用方式在所有&#34; f&#34;重载。 请注意,使用非模板代码时不会重现该错误:

void b(const int a, const std::function< bool(const int& v) >& validator)
{
    if (validator(a))
    {
        std::cout << a;
    }
}

void b(const int a)
{
    std::cout << a;
}

// ...
b(5, [](const int& a){ return a != 0; });
b(5);

结论:使用std :: function作为模板函数中的参数,该函数被非模板函数重载,会产生意外错误的重载函数推导结果。

问题:如何将std :: function作为参数传递给模板函数,而不会牺牲不必显式声明函数模板参数的便利性?

以下是重现编译器错误的代码:

#include <iostream>
#include <functional>

#define USE_FUNCTOR
#ifdef USE_FUNCTOR
template< typename T >
void f(const T& v, const std::function< bool( const T& ) >& validator )
{
    if (validator(v))
    {
        std::cout << v;
    }
}
#else
template< typename T >
void f(const T& v, bool(*validator)(const T& a))
{
    if ((*validator)(v))
    {
        std::cout << v;
    }
}
#endif

void f(const bool& v)
{
    std::cout << (v ? "yes" : "no" );
}

bool isIntValid(const int& a)
{
    return a != 0;
}

int main(int argc, char* argv[])
{
#ifdef USE_FUNCTOR
    f(5, [](const int& a){ return a != 0; });
#endif
    f(5, & isIntValid );
    f(true);

    return EXIT_SUCCESS;
}

2 个答案:

答案 0 :(得分:1)

问题是T在参数v和参数validator中的推断上下文中,这意味着调用者必须提供实际的std::function扣除工作的对象。 lambda 类型为std::function

由于您只需要对第一个参数进行演绎,因此您需要一个帮助您进行非演绎的第二个T事件:

template <class T>
struct NonDeduced
{
  typedef T type;
};

template <class T>
void f(const T& v, const std::function< bool( const typename NonDeduced<T>::type& ) >& validator );

[Live example]


作为旁注,您勾画的“已删除的专业化”的语法不正确。它将是:

template<>
void f(const bool& v, const std::function< bool(const bool&) >& validator) = delete;

答案 1 :(得分:0)

虽然Microsoft编译器给出了一个非常误导性的错误,但拒绝您的代码是正确的。

不会选择两个参数版本,因为您的lambda或函数指针不能用于推导std::function的模板参数。

我建议将函数类型设为模板参数:

template< typename T, typename F >
void f(const T& v, F validator )
{
    if (validator(v))
    {
        std::cout << v;
    }
}

Clang对错误更清楚:

main.cpp:7:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-0 &)>' against '(lambda at main.cpp:38:10)'
void f(const T& v, const std::function< bool( const T& ) >& validator )

main.cpp:7:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-0 &)>' against 'bool (*)(const int &)'
void f(const T& v, const std::function< bool( const T& ) >& validator )