Рассмотрим следующий пример:
#include <iostream>
struct A{
A() = default;
A(A const&){}
};
struct B:A{};
struct C{
explicit operator B(){
return B{};
}
};
int main(){
C c;
A a(c); // #1
}
GCC
и Clang
оба сообщают, что c
нельзя преобразовать в const A&
. Однако в стандарте есть соответствующее правило: explicit operator B()
должна быть функцией-кандидатом в этом контексте. То есть:
over.match.copy # 1.2
При инициализации временного объекта, который должен быть привязан к первому параметру конструктора, где параметр имеет тип «ссылка на возможно cv-квалифицированный T» и конструктор вызывается с одним аргументом в контексте прямой инициализации объекта объект типа «cv2 T», также рассматриваются функции явного преобразования. Те, которые не скрыты в S и дают тип, чья cv-неквалифицированная версия является тем же типом, что и T, или является его производным классом, являются функциями-кандидатами.
Более того, в текущем стандарте соответствующее правило по-прежнему имеет то же значение:
over.match.copy # 1.2
Когда тип выражения инициализатора является типом класса «cv S», рассматриваются функции преобразования. Допустимые типы для неявных функций преобразования: T и любой класс, производный от T. При инициализации временного объекта ([class.mem]) для привязки к первому параметру конструктора, где параметр имеет тип «ссылка на cv2 T», а конструктор вызывается с одним аргументом в контексте прямая инициализация объекта типа «cv3 T», допустимые типы для явных функций преобразования одинаковы; в противном случае их нет.
Независимо от стандарта C ++ 17 или текущего стандарта, все они говорят, что в контексте, подобном #1
, функция явного преобразования, которая имеет возвращаемый тип класса T
или любого класса, производного от T
, также является кандидатом.
В моем примере объект a
типа A
напрямую инициализируется единственным инициализатором c
, который является единственным аргументом в вызове конструктора, конструктор копирования A
имеет параметр A const&
. В этом случае c
следует использовать для инициализации временного объекта, который будет привязан к первому параметру. Следовательно, в этом случае explicit operator B()
следует рассматривать как функцию-кандидат. Однако и GCC
, и Clang
отвергают этот пример. GCC
очевидно, что сообщают о бессмысленной информации о dignosis.
Это ошибка этих компиляторов? Или я что-то неправильно понял?
explicit
или вернуA
из преобразования, оно будет скомпилировано. Насколько я понимаю, часть cv2 T ... cv3 T подразумевает, что оператор явного преобразования должен выдавать T, а не S (который будет в категории: в противном случае их нет). Боковое примечание: цель явного - избежать нежелательного преобразования, и для меня нарезка вообще нежелательна ... - person Phil1970   schedule 02.01.2021S
. Обратите внимание на эту формулировку: допустимые типы для функций неявного преобразования - это T, и любой класс, производный от T, и допустимые типы для функций явного преобразования одинаковы; . Таким образом, следует учитывать не толькоT
, но и любой тип, производный отT
. - person xmh0511   schedule 02.01.2021A
будетT
, аB
будетS
. Поскольку ваша конверсия является явной, у вас есть cv3 S в качестве возврата вашей конверсии, и вы попадаете в категорию, в противном случае нет никакой категории. Я почти уверен, что новая формулировка исправит нежелательное преобразование в вашем коде. Если вы действительно хотите это преобразование, вы либо удалитеexplicit
, либо добавите преобразование вA
. - person Phil1970   schedule 02.01.2021C
вB
иB
вA
. При использованииexplicit
обычно требуется только одно преобразование. В противном случае скажите мне разницу между явным или неявным. - person Phil1970   schedule 02.01.2021explicit conversion functions
рассматривается как кандидат при выполнении прямой инициализации. Однако [over.match.copy # 1.2] - это специальное правило, которое разрешает явной функции преобразования быть кандидатом при инициализации параметра (инициализация копирования), пока объект инициализируется. неквалифицированный тип cv совпадает с типом, на который ссылается параметр конструктора - person xmh0511   schedule 03.01.2021a
имеет тип класса A, выражение инициализатораc
имеет тип класса C, а параметр конструктора копирования - ссылка на const A. Итак, при инициализации временного объекта ... примените здесь. ВзятыеA
какT
и ВзятыеC
какS
. C ++ 17 говорит, что те функции преобразования, возвращаемый тип которых T или любой тип класса, производный от T, являются функциями-кандидатами. C ++ 20 говорит, что допустимые типы для явных функций преобразования одинаковы. Итак, расплывчатость наthe same
. Мое чтение такое же, как и для функций преобразования - person xmh0511   schedule 03.01.2021A
is, почему бы неB
, который является производнымA
. РазвеB
не-А? - person xmh0511   schedule 06.01.2021A a(c);
, где объектa
инициализируется напрямую инициализаторомc
, поэтому сначала примените [dcl.init.general # 15.6.2], чтобы выбрать конструктор для этой инициализации. . Принимаяc
в качестве аргумента для вызова конструктораA(A const&)
, где параметр инициализируется копией из аргумента согласно dcl.init # general-13. Следовательно, это не прямая инициализация, и параметр не привязан к временному. Так что особых правил здесь не должно быть. - person xmh0511   schedule 07.01.2021