std :: function作为模板参数

huy*_*itw 14 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;
    }
};
Run Code Online (Sandbox Code Playgroud)

并使用它像:

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"
Run Code Online (Sandbox Code Playgroud)

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

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

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

错误C2593:'operator = is ambiguous'

IntelliSense提供了更多信息:

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

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

Yak*_*ont 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*/);
Run Code Online (Sandbox Code Playgroud)

基本上我们正在做手动重载决议.

更先进的技术:(可能不需要)

另一种方法是限制std::function =使用SFINAE对被调用的参数有效,但这更加混乱.

如果您有多种不同类型与您竞争,您std::function必须手动调度所有这些类型.解决这个问题的方法是测试你的类型U是否可以调用,没有任何内容,结果可以转换为T,然后标记为dispatch.坚持std::function替代分支中的非重载,并让其他一切通常更传统的重载.

有一个细微的区别在于,一个可转换为两者的类型std::wstring和可调用的返回可转换的东西T最终被分派到不同于上述原始简单解决方案的重载,因为所使用的测试实际上并不相互排斥.对于C++重载的完全手动模拟(纠正std::functions愚蠢),你需要使这种情况模糊不清!

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


Dav*_*eas 6

双方std::functionstd::wstring已完成转换运营商,可以采取你逝去的字面宽字符串.在这两种情况下,转换都是用户定义的,因此转换序列具有相同的优先级,从而导致歧义.这是错误的根本原因.