我目前有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
?我该如何解决这个问题?
答案 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::function
和std::wstring
都有转换运算符,可以使用您传递的文字宽字符串。在这两种情况下,转换都是用户定义的,因此转换序列具有相同的优先级,从而导致歧义。这是错误的根本原因。