std :: function作为模板参数

时间:2013-12-16 14:02:27

标签: c++ templates c++11 std-function

我目前有map<int, std::wstring>,但为了灵活性,我希望能够分配一个lambda表达式,并在地图中返回std::wstring作为值。

所以我创建了这个模板类:

template <typename T>
class ValueOrFunction
{
private:
    std::function<T()> m_func;
public:
    ValueOrFunction() : m_func(std::function<T()>()) {}
    ValueOrFunction(std::function<T()> func) : m_func(func) {}
    T operator()() { return m_func(); }
    ValueOrFunction& operator= (const T& other)
    {
        m_func = [other]() -> T { return other; };
        return *this;
    }
};

并使用它:

typedef ValueOrFunction<std::wstring> ConfigurationValue;
std::map<int, ConfigurationValue> mymap;

mymap[123] = ConfigurationValue([]() -> std::wstring { return L"test"; });
mymap[124] = L"blablabla";
std::wcout << mymap[123]().c_str() << std::endl; // outputs "test"
std::wcout << mymap[124]().c_str() << std::endl; // outputs "blablabla"

现在,我不想使用构造函数来包装lambda,所以我决定添加第二个赋值运算符,这次是std::function

ValueOrFunction& operator= (const std::function<T()>& other)
{
    m_func = other;
    return *this;
}

这是编译器开始抱怨的地方。第mymap[124] = L"blablabla";行突然导致此错误:

  

错误C2593:'operator = is ambiguous'

IntelliSense提供了更多信息:

  

多个运算符“=”匹配这些操作数:function   “ValueOrFunction :: operator =(const std :: function&amp; other)[with   T = std :: wstring]“function”ValueOrFunction :: operator =(const T   &amp; other)[with T = std :: wstring]“操作数类型是:ConfigurationValue =   const wchar_t   [10] c:\ projects \ beta \ CppTest \ CppTest \ CppTest.cpp 37 13 CppTest

所以,我的问题是,为什么编译器无法区分std::function<T()>T?我该如何解决这个问题?

2 个答案:

答案 0 :(得分:12)

基本问题是std::function有一个贪婪的隐式构造函数,它会尝试转换任何,而且只能在正文中编译。因此,如果您想要重载它,或者不允许转换为替代方案,您需要禁用可以通过调用std::function重载转换为替代方法的内容。

最简单的技术是标签调度。设置一个贪婪的operator=并设置为完美转发,然后手动调度到带有标记的assign方法:

 template<typename U>
 void operator=(U&&u){
   assign(std::forward<U>(u), std::is_convertible<U, std::wstring>());
 }
 void assign(std::wstring, std::true_type /*assign_to_string*/);
 void assign(std::function<blah>, std::false_type /*assign_to_non_string*/);

基本上我们正在进行手动重载解析。

更高级的技巧:(可能不需要)

另一种方法是使用SFINAE限制std::function =对被调用的参数有效,但这样更麻烦。

如果您有多种不同类型与std::function竞争,则必须手动分发所有这些类型。解决这个问题的方法是测试你的类型U是否可以无需调用,结果可以转换为T,然后对其进行标记调度。将非std::function重载粘贴在替代分支中,让其他一切更常见的传统重载。

有一个微妙的区别在于,一个可转换为std::wstring的类型和可转换的可转换为T的类型最终被分派到不同于上述原始简单解决方案的重载,因为所使用的测试是实际上并不相互排斥。对于C ++重载的完全手动模拟(针对std::function愚蠢进行了更正),您需要使 案例模糊不清!

最后一个高级操作是使用auto和尾随返回类型来提高其他代码检测=是否有效的能力。就个人而言,我不会在C ++ 14之前做到这一点,除非是在胁迫下,除非我正在编写一些严肃的库代码。

答案 1 :(得分:6)

std::functionstd::wstring都有转换运算符,可以使用您传递的文字宽字符串。在这两种情况下,转换都是用户定义的,因此转换序列具有相同的优先级,从而导致歧义。这是错误的根本原因。