初始化和lambda类型参数

时间:2015-03-05 10:12:58

标签: c++ c++11 constructor lambda initialization

我有这样一个实用类:

struct Atreturn
{
    std::function<void()> funcdestr;
    Atreturn( std::function<void()> fd ): funcdestr(fd) {}
    ~Atreturn() { funcdestr(); }
};

注意构造函数中没有explicit属性。

可能的用途应该是:

  1. 直接初始化构造函数调用:

    Atreturn hook ( [something]() { DestroySomething(something); } );
    
  2. 复制初始化构造函数调用:

    Atreturn hook = [something]() { DestroySomething(something); };
    
  3. 直接列表初始化构造函数调用:

    Atreturn hook { [something]() { DestroySomething(something); }};
    
  4. 现在的问题是:据我所知,方法#1和#2应该被允许,因为它们在理论上是相同的,只要构造函数中没有explicit,而不应该允许#3因为这种语法可以阻止转换(如果您尝试int,至少对于int{2.1}也是如此。)

    但是,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); };
    

1 个答案:

答案 0 :(得分:7)

  

虽然不应该允许#3,因为这种语法会阻止转换(如果你尝试使用int {2.1},至少对于int来说也是如此)。

那不太对劲。规则是不允许缩小转化次数。允许其他类型的转换。 int{2.1}是一种缩小转换,因为它会修改值,从而失去精度。 int{2.0}不是缩小转化,因为价值不会改变。

#2失败的原因是它需要两个隐含的用户定义转换,这是非法的。

从概念上讲,复制初始化如:

Atreturn hook = []() {};

相当于:

Atreturn hook = Atreturn([]() {});

(除了它不能调用&#39;显式&#39;构造函数,并允许编译器忽略副本)。

这意味着首先lambda必须隐式转换为function<void()>,然后必须隐式转换为Atreturn。这两种转换都是&#34;用户定义的转换序列&#34;这意味着他们称之为构造函数,而不是intlong之类的内置转换,标准表示隐式转换序列不能包含多个用户定义的转换。

问题实际上与lambdas无关,你可以像这样展示完全相同的错误:

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()>