【问题标题】:std::function as template parameterstd::function 作为模板参数
【发布时间】: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&lt;T()&gt; 和T?我该如何解决这个问题?

【问题讨论】:

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


    【解决方案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::functions 的愚蠢进行了更正),您需要使 那个 的大小写模棱两可!

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

    【讨论】:

    • 我在考虑 SFINAE 是否有意义,我选择不建议它过于复杂。这里练习的标签调度是一个很好的干净解决方案+1(尽管它值得更多)
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-10
      • 1970-01-01
      • 2021-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多