Ես միշտ ենթադրում էի, որ ժամանակավոր օբյեկտները ապրում են մինչև ամբողջական արտահայտման ավարտը: Այնուամենայնիվ, այստեղ կա հետաքրքիր տարբերություն std::vector
-ի և զանգվածի սկզբնավորումների միջև:
Խնդրում ենք հաշվի առնել հետևյալ կոդը.
#include <iostream>
#include <vector>
struct ID{
static int cnt;
// the number of living object of class ID at the moment of creation:
int id;
ID():id(++cnt){}
~ID(){
cnt--;
}
};
int ID::cnt=0;
int main(){
int arr[]{ID().id, ID().id};
std::vector<int> vec{ID().id, ID().id};
std::cout<<" Array: "<<arr[0]<<", "<<arr[1]<<"\n";
std::cout<<" Vector: "<<vec[0]<<", "<<vec[1]<<"\n";
}
Այս ծրագրի արդյունքը մի փոքր (գոնե ինձ համար) անսպասելի է.
Array: 1, 1
Vector: 1, 2
Դա նշանակում է, որ ժամանակավոր օբյեկտները կենդանի են std::vector
-ի սկզբնավորման ողջ ընթացքում, բայց զանգվածի դեպքում դրանք ստեղծվում և ոչնչացվում են մեկը մյուսի հետևից: Ես կակնկալեի, որ ժամանակավորները կապրեն մինչև int arr[]{ID().id, ID().id};
ամբողջական արտահայտությունը ավարտվի:
Ստանդարտը նշում է մեկ բացառություն՝ կապված ժամանակավոր օբյեկտների կյանքի տևողության և զանգվածների սկզբնավորման հետ (12.2): Սակայն ես չեմ հասկանում դրա իմաստը և չգիտեմ, թե ինչու է այն կիրառվում տվյալ դեպքում.
Գոյություն ունեն երկու համատեքստ, որոնցում ժամանակավորները ոչնչացվում են լրիվ արտահայտման ավարտից տարբեր կետում: Առաջին համատեքստն այն է, երբ լռելյայն կոնստրուկտորը կանչվում է զանգվածի տարրը սկզբնավորելու համար: Եթե կոնստրուկտորն ունի մեկ կամ մի քանի լռելյայն արգումենտ, ապա լռելյայն արգումենտում ստեղծված յուրաքանչյուր ժամանակավոր արգումենտի ոչնչացումը հաջորդականացվում է զանգվածի հաջորդ տարրի կառուցումից առաջ, եթե այդպիսիք կան:
Արդյունքների ակնարկ տարբեր կոմպիլյատորների միջոցով (MSVS արդյունքը NathanOliver-ի ակնարկն է).
Array Vector
clang 3.8 1, 2 1, 2
g++ 6.1 1, 1 1, 2
icpc 16 1, 1 1, 2
MSVS 2015 1, 1 1, 2
Ինչպես նշեց Էկատմուրը, ագրեգատային սկզբնավորման համար braced-init-list-ի յուրաքանչյուր տարր ամբողջական արտահայտություն է, հետևաբար հետևյալ կոդը.
struct S{
int a;
int b;
} s{ID().id, ID().id};
std::cout<<" Struct: "<<s.a<<", "<<s.b<<"\n";
պետք է տպել Struct 1, 1
վահանակի վրա: Դա հենց այն է, ինչ անում է g++-ի կազմած ծրագիրը։ Այնուամենայնիվ, clang-ը կարծես սխալ ունի. արդյունքում ստացված ծրագիրը տպում է Struct 1, 2
:
Հաղորդվել է, որ սխալ է հնչել՝ https://llvm.org/bugs/show_bug.cgi?id=29080
S::S(int)
ին: Init-հայտարարիչը կոնստրուկտ չէ, որը սահմանված է ֆունկցիայի անուղղակի կանչ ստեղծելու համար. այդպիսի օրինակ կլինիif
հայտարարությունը, որը սահմանվում է անուղղակիորեն փոխակերպելու իրcondition
-ի (արտահայտության) արդյունքըbool
-ի: 11.08.2017