初始化和 lambda 类型参数
Initialization and lambda-type argument
我有这样的实用程序class:
struct Atreturn
{
std::function<void()> funcdestr;
Atreturn( std::function<void()> fd ): funcdestr(fd) {}
~Atreturn() { funcdestr(); }
};
注意构造函数中没有 explicit
属性。
可能的用法应该是:
直接初始化构造函数调用:
Atreturn hook ( [something]() { DestroySomething(something); } );
复制初始化构造函数调用:
Atreturn hook = [something]() { DestroySomething(something); };
直接列表初始化构造函数调用:
Atreturn hook { [something]() { DestroySomething(something); }};
现在的问题是:据我所知,方法 #1 和 #2 应该被允许,因为它们在理论上是相同的,前提是构造函数中没有 explicit
,而 #3 不应该被允许,因为这种语法会阻止转换(如果您尝试 int{2.1}
,至少对于 int
是这样)。
然而,gcc 4.9 允许方法#1 和#3,但不允许#2(并且说 conversion from '...::<lambda()>' to non-scalar 'Atreturn' type requested
)。这听起来很疯狂,因为它通常只有在你有 explicit
构造函数时才会发生。谁能解释一下,为什么?
此外,让我更明确地说明这个问题:我需要一些不太笨拙的语法来初始化这个 Atreturn 对象,至少不需要额外的大括号或圆括号。问题在于,当参数为 C++11 lambda 时,具有自动缩进功能的编辑器无法正确重新缩进。所以我需要一些可以表示为的语法:
Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); };
while #3 should not be allowed because this syntax prevents conversions (at least it's so for int if you tried int{2.1}).
这不太正确。规则是不允许缩小转换。允许其他类型的转换。 int{2.1}
是一个缩小转换,因为它修改了值,失去了精度。 int{2.0}
不是缩小转换,因为值没有改变。
#2 失败的原因是它需要两个隐式的用户定义转换,这是被禁止的。
从概念上讲,复制初始化如下:
Atreturn hook = []() {};
相当于:
Atreturn hook = Atreturn([]() {});
(除了它不能调用'explicit'构造函数,编译器允许省略副本)。
这意味着首先 lambda 必须隐式转换为 function<void()>
,然后必须隐式转换为 Atreturn
。这两个转换都是 "user-defined conversion sequence" 意味着它们调用构造函数,而不是像 int
到 long
这样的内置转换,并且标准规定隐式转换序列不能包含多个用户-定义转换。
问题实际上与 lambda 无关,您可以像这样演示完全相同的错误:
struct L { };
struct F { F(L) { } };
struct A { A(F) { } };
A a = L();
l.cc:4:9: error: conversion from ‘L’ to non-scalar type ‘A’ requested
A a = L();
^
同样,问题是隐式转换序列L -> F -> A
涉及到两个用户定义的转换,这是被禁止的。
对于您想要调整代码以帮助自动缩进的问题,我不太赞同 - 不应破坏代码以适应有缺陷的编辑器。但是,另一种选择是添加一个模板构造函数,它接受任何可以转换为 std::function<void()>
的内容,例如
struct Atreturn
{
using func_type = std::function<void()>;
template<typename T,
typename Requires = decltype(func_type(std::declval<T&&>())>
Atreturn(T t) : funcdestr(std::move(t)) { }
...
};
这将允许将 lambda 直接转换为 Atreturn
,而无需先隐式转换为 function<void()>
。
我有这样的实用程序class:
struct Atreturn
{
std::function<void()> funcdestr;
Atreturn( std::function<void()> fd ): funcdestr(fd) {}
~Atreturn() { funcdestr(); }
};
注意构造函数中没有 explicit
属性。
可能的用法应该是:
直接初始化构造函数调用:
Atreturn hook ( [something]() { DestroySomething(something); } );
复制初始化构造函数调用:
Atreturn hook = [something]() { DestroySomething(something); };
直接列表初始化构造函数调用:
Atreturn hook { [something]() { DestroySomething(something); }};
现在的问题是:据我所知,方法 #1 和 #2 应该被允许,因为它们在理论上是相同的,前提是构造函数中没有 explicit
,而 #3 不应该被允许,因为这种语法会阻止转换(如果您尝试 int{2.1}
,至少对于 int
是这样)。
然而,gcc 4.9 允许方法#1 和#3,但不允许#2(并且说 conversion from '...::<lambda()>' to non-scalar 'Atreturn' type requested
)。这听起来很疯狂,因为它通常只有在你有 explicit
构造函数时才会发生。谁能解释一下,为什么?
此外,让我更明确地说明这个问题:我需要一些不太笨拙的语法来初始化这个 Atreturn 对象,至少不需要额外的大括号或圆括号。问题在于,当参数为 C++11 lambda 时,具有自动缩进功能的编辑器无法正确重新缩进。所以我需要一些可以表示为的语法:
Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); };
while #3 should not be allowed because this syntax prevents conversions (at least it's so for int if you tried int{2.1}).
这不太正确。规则是不允许缩小转换。允许其他类型的转换。 int{2.1}
是一个缩小转换,因为它修改了值,失去了精度。 int{2.0}
不是缩小转换,因为值没有改变。
#2 失败的原因是它需要两个隐式的用户定义转换,这是被禁止的。
从概念上讲,复制初始化如下:
Atreturn hook = []() {};
相当于:
Atreturn hook = Atreturn([]() {});
(除了它不能调用'explicit'构造函数,编译器允许省略副本)。
这意味着首先 lambda 必须隐式转换为 function<void()>
,然后必须隐式转换为 Atreturn
。这两个转换都是 "user-defined conversion sequence" 意味着它们调用构造函数,而不是像 int
到 long
这样的内置转换,并且标准规定隐式转换序列不能包含多个用户-定义转换。
问题实际上与 lambda 无关,您可以像这样演示完全相同的错误:
struct L { };
struct F { F(L) { } };
struct A { A(F) { } };
A a = L();
l.cc:4:9: error: conversion from ‘L’ to non-scalar type ‘A’ requested
A a = L();
^
同样,问题是隐式转换序列L -> F -> A
涉及到两个用户定义的转换,这是被禁止的。
对于您想要调整代码以帮助自动缩进的问题,我不太赞同 - 不应破坏代码以适应有缺陷的编辑器。但是,另一种选择是添加一个模板构造函数,它接受任何可以转换为 std::function<void()>
的内容,例如
struct Atreturn
{
using func_type = std::function<void()>;
template<typename T,
typename Requires = decltype(func_type(std::declval<T&&>())>
Atreturn(T t) : funcdestr(std::move(t)) { }
...
};
这将允许将 lambda 直接转换为 Atreturn
,而无需先隐式转换为 function<void()>
。