std::function 作为模板参数
Posted
技术标签:
【中文标题】std::function 作为模板参数【英文标题】:std::function as template parameter 【发布时间】:2014-01-03 22:52:35 【问题描述】:我目前有一个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:“运算符 = 不明确”
IntelliSense 提供了更多信息:
多个运算符“=”匹配这些操作数:函数 "ValueOrFunction::operator=(const std::function &other) [with T=std::wstring]" 函数 "ValueOrFunction::operator=(const T &other) [with T=std::wstring]" 操作数类型为:ConfigurationValue = 常量 wchar_t [10] c:\projects\beta\CppTest\CppTest\CppTest.cpp 37 13 CppTest
那么,我的问题是,为什么编译器不能区分std::function<T()>
和T
?我该如何解决这个问题?
【问题讨论】:
【参考方案1】:基本问题是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
s 的愚蠢进行了更正),您需要使 那个 的大小写模棱两可!
最后要做的高级事情是使用auto
和尾随返回类型来提高其他代码检测您的=
是否有效的能力。就个人而言,除非迫不得已,否则我不会在 C++14 之前这样做,除非我正在编写一些严肃的库代码。
【讨论】:
我在考虑 SFINAE 是否有意义,我选择不建议它过于复杂。这里练习的标签调度是一个很好的干净解决方案+1(尽管它值得更多)【参考方案2】:std::function
和 std::wstring
都有转换运算符,可以采用您传递的文字宽字符串。在这两种情况下,转换都是用户定义的,因此转换顺序具有相同的优先级,从而导致歧义。这是错误的根本原因。
【讨论】:
以上是关于std::function 作为模板参数的主要内容,如果未能解决你的问题,请参考以下文章
模板函数替换仅在一个参数中未使用std :: function时才起作用
C++11 std::bind函数,std::function函数
C++11:std::function<void()> func;