std :: function作为模板参数 [英] std::function as template parameter

查看:558
本文介绍了std :: function作为模板参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I currently have a map<int, std::wstring>, but for flexibility, I want to be able to assign a lambda expression, returning std::wstring as value in the map.

因此,我创建了此模板类:

So I created this template class:

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

Now, I don't want to use the constructor for wrapping the lambda, so I decided to add a second assignment operator, this time for the std::function:

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

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

This is the point where the compiler starts complaining. The line mymap[124] = L"blablabla"; suddenly results in this error:


错误C2593:'运算符=不明确'

error C2593: 'operator = is ambiguous'

IntelliSense提供了更多信息:

IntelliSense gives some more info:


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

more than one operator "=" matches these operands: function "ValueOrFunction::operator=(const std::function &other) [with T=std::wstring]" function "ValueOrFunction::operator=(const T &other) [with T=std::wstring]" operand types are: ConfigurationValue = const wchar_t [10] c:\projects\beta\CppTest\CppTest\CppTest.cpp 37 13 CppTest

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

So, my question is, why isn't the compiler able to distinguish between std::function<T()> and T? And how can I fix this?

推荐答案

基本问题是 std :: function 有一个贪婪的隐式构造函数,该构造函数将尝试转换任何东西,并且只会在主体中无法编译。因此,如果您想重载它,则不允许转换为替代方法,而您需要禁用可以转换为替代方法的东西,而不调用 std :: function 重载。

The basic problem is that std::function has a greedy implicit constructor that will attempt to convert anything, and only fail to compile in the body. So if you want to overload with it, either no conversion to the alternative can be allowed, of you need to disable stuff that can convert to the alternative from calling the std::function overload.

最简单的技术是标记分派。制作一个贪婪的 operator = 并进行完美的转发,然后手动将其分配给带有标签的 assign 方法:

The easiest technique would be tag dispatching. Make an operator= that is greedy and set up for perfect forwarding, then manually dispatch to an assign method with a tag:

 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*/);

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

basically we are doing manual overload resolution.

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

More advanced techniques: (probably not needed)

另一种方法是限制 std :: function = 在调用的参数上带有SFINAE是有效的,但这比较混乱。

Another approach would be to limit the std::function = with SFINAE on the argument being invoked is valid, but that is messier.

如果您有多种不同的类型与 std :: function 竞争时,您必须手动分配所有它们。解决该问题的方法是测试您的类型 U 是否可无任何调用,并且结果可转换为 T ,然后标签派遣。将非 std :: function 重载保留在Alternative分支中,并让通常的更传统的重载发生在其他所有事物上。

If you have multiple different types competing with your std::function you have to sadly manually dispatch all of them. The way to fix that is to test if your type U is callable with nothing and the result convertible to T, then tag dispatch on that. Stick the non-std::function overloads in the alternative branch, and let usual more traditional overloading to occur for everything else.

有一个微妙的区别,即类型可转换为 std :: wstring 且可调用的类型返回可转换为 T 最终被分派到与上述原始简单解决方案不同的重载,因为所使用的测试实际上并不是互斥的。对于C ++重载的完整手动仿真(已针对 std :: function 的愚蠢进行了纠正),您需要使那个的情况不明确!

There is a subtle difference in that a type convertible to both std::wstring and callable returning something convertible to T ends up being dispatched to different overloads than the original simple solution above, because the tests used are not actually mutually exclusive. For full manual emulation of C++ overloading (corrected for std::functions stupidity) you need to make that case ambiguous!

最后要做的高级事情是使用 auto 并尾随返回类型,以提高其他代码检测您的 = 有效。就个人而言,除非受到胁迫,否则我不会在C ++ 14之前执行此操作,除非我正在编写一些严肃的库代码。

The last advanced thing to do would be to use auto and trailing return types to improve the ability of other code to detect if your = is valid. Personally, I would not do this before C++14 except under duress, unless I was writing some serious library code.

这篇关于std :: function作为模板参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆