2017-09-06 23 views
15

当我在统一初始化过程中使用尾随逗号时,是否有任何潜在的语义差异?统一初始化中的尾随逗号

std::vector<std::size_t> v1{5, }; // allowed syntax 
std::vector<std::size_t> v2{10}; 

我能否使用尾随逗号使编译器来选择std::vector::vector(std::initializer_list<std::size_t>)构造函数,而不是std::vector::vector(std::size_t, const std::size_t &)还是有什么其他的技巧与提到的语法?

我可以用它来检测是否有std::initializer_list -constructor重载?

考虑下面的代码,必须选择哪个构造函数?

struct A { A(int) { ; } A(double, int = 3) { ; } }; 
A a{1}; 
A b{2, }; 

此代码is accepted by gcc 8A(int)在两种情况下被选中。

+0

第一个('std :: initializer_list'-constructor)总是s当选。 https://wandbox.org/permlink/Yy6VtcK5ISCbzZJp – songyuanyao

+0

无论如何,没有理由'v2 {...}'调用任何构造函数,但只有'std :: initializer_list' ....只有'v2(。 ..)'会调用'std :: vector :: vector(std :: size_t ...)' – jpo38

+0

@songyuanyao是的,但如果只有构造函数具有一元和二元的arities,可能的,默认的函数参数初始值设定项和其他情况?在所有可想象的情况下,有没有*语义差异? – Orient

回答

9

首先,C++语法规则使尾随,可选,用于括号初始化列表。引用dcl.init/1

声明者可以为标识为 的声明指定初始值。标识符指定正在初始化的变量。在[dcl.init] 的其余部分中描述的初始化过程也适用于由其他语法上下文指定的初始化,例如函数参数初始化([expr.call])或返回值初始化([ stmt.return])。

initializer: 
    brace-or-equal-initializer 
    (expression-list) 
brace-or-equal-initializer: 
    = initializer-clause 
    braced-init-list 
initializer-clause: 
    assignment-expression 
    braced-init-list 
braced-init-list: 
    { initializer-list ,opt } 
    { designated-initializer-list ,opt } 
    { } 

其次,你不能几乎覆盖重载解析系统。如果使用这种语法,它将始终使用std::initializer_list构造函数,并且此类构造函数可用。

dcl.init.list/2

构造函数是一个初始化列表构造如果它的第一个参数 是类型为std :: initializer_list或参照 可能CV-合格的std :: initializer_list对于一些键入E,并且 要么没有其他参数,要么所有其他参数都具有默认参数 。 [注意:初始化列表构造函数比list-initialization([over.match.list])中的其他构造函数更受青睐....


下面打印Using InitList程序:

#include <iostream> 
#include <initializer_list> 

struct X{ 
    X(std::initializer_list<double>){ std::cout << "Using InitList\n"; } 
    X(int){ std::cout << "Using Single Arg ctor\n"; } 
}; 

int main(){ 
    X x{5}; 
} 

尽管5是文字int型的,它应该是有意义的选择,因为它是一个单参数的构造函数完美搭配;并且std::initializer_list<double>构造函数想要一个double的列表。但是,规则有利于std::initializer_list<double>,因为它的初始值设定项列表构造函数

其结果是,即使下面的程序将失败,因为收缩转换:

#include <iostream> 
#include <initializer_list> 

struct Y{ 
    Y(std::initializer_list<char>){ std::cout << "Y Using InitList\n"; } 
    Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; } 
}; 

int main(){ 
    Y y1{4777}; 
    Y y2{577,}; 
    Y y3{57,7777}; 
} 

在回答下面的评论,“如果有一种是的std :: initializer_list没有超载,或它不是第一个构造函数的参数?“ - 然后重载解析不会选择它。演示:

#include <iostream> 
#include <initializer_list> 

struct Y{ 
    Y(int, std::initializer_list<double>){ std::cout << "Y Using InitList\n"; } 
    Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; } 
}; 

int main(){ 
    Y y1{4}; 
    Y y2{5,}; 
    Y y3{5,7}; 
} 

打印:

Y Using Double Arg ctor 
Y Using Double Arg ctor 
Y Using Double Arg ctor 

如果没有初始化列表构造可用,那么{initializer-list...,}初始化几乎回落到直接初始化dcl.init/16,其语义包含在前面的段落dcl.init/16

+0

好的,如果有没有用'std :: initializer_list'重载,或者它不是第一个构造函数的参数? – Orient

+0

@Orient,看到我更新的答案 – WhiZTiM

2

不是。该逗号是让预处理器宏技巧无编译错误的让步。这对于您的数据类型或其大小没有任何意义。