Чи повинен бути T повного типу, щоб використовувати його в `std :: declval <T>`?


11

Розглянемо цей приклад ( звідси ):

#include <type_traits>
#include <iostream>
template <typename U>
struct A {
};

struct B {
   template <typename F = int>
   A<F> f() { return A<F>{}; }

   using default_return_type = decltype(std::declval<B>().f());
};

int main()
{
    B::default_return_type x{};
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
}

Він компілюється без помилок на gcc9.2, але gcc7.2 та clang 10.0.0 скаржаться на неповне Bзавершення. Помилка Клангса:

prog.cc:11:58: error: member access into incomplete type 'B'
   using default_return_type = decltype(std::declval<B>().f());
                                                         ^
prog.cc:7:8: note: definition of 'B' is not complete until the closing '}'
struct B {
       ^
prog.cc:16:8: error: no type named 'default_return_type' in 'B'
    B::default_return_type x{};
    ~~~^
prog.cc:17:35: error: no member named 'default_return_type' in 'B'
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
                               ~~~^

1
Здається, заголовок питання не відповідає помилці? Мені здається, на це скаржиться GCC .f(). Що має сенс; неповний тип Bне має члена f.
MSalters

@MSalters Я думав те саме, але тоді яка реальна проблема тут? Я б припустив, що як тільки ви отримаєте екземпляр від std::declvalнього, це вже не має значення, якщо тип був повним чи ні (і, мабуть, я з цим помиляюся)
idclev 463035818

[expr.ref] / 2 (C ++ 11) говорить про доступ члена класу: "Для першого параметра (крапки) перший вираз повинен мати повний тип класу" . І Bне є ні повною, ні вважається завершеною в Росії alias-declaration.
юрист з мови


1
@LanguageLawyer добре, тоді я погоджуюся, що моя інтерпретація була вимкнена, і, здається, щось змінилося з моменту c ++ 11, що робить вищезгадане нормально в нових стандартах, але не в c ++ 11. Ви б не хотіли написати відповідь?
idclev 463035818

Відповіді:


9

Джерелом помилки не є std::declval, а неповний доступ для учасників класу.

До тих пір, поки резолюцію CWG1836 не було об'єднано 2,5 роки тому, стандарт вимагав, щоб клас був завершений у виразі доступу до класу ( E1.E2).
[expr.ref] / 2 в C ++ 11 :

Для першого варіанту (крапка) перший вираз повинен мати повний тип класу.

[expr.ref] / 2 в C ++ 17 :

Для першого варіанту (крапка) перший вираз має бути glvalue, що має тип повного класу.

І клас не вважається закінченим в alias-declarationмежах власного member-specification.
[class.mem] / 6 в C ++ 17 :

Клас вважається повністю певним типом об'єкта ([basic.types]) (або повний типу) при закритті }цього класу специфікатор . У специфікації члена класу клас вважається завершеним у функціональних органах, аргументах за замовчуванням, специфікаторами noexcept і ініціалізаторами за замовчуванням (включаючи такі речі в вкладені класи). В іншому випадку він вважається неповним у специфікації його власного класу .


8

Від [declval] :

Примітка: Параметр шаблону Tз declvalможе бути неповним типом.

Таке формулювання існує з C ++ 11 (тому компілятори не можуть відповідати попередньому стандарту)


дивним, ось що я сподівався. Схоже, що gcc це виправив, кланг (ще)
idclev 463035818

@ bivlyknownas_463035818: Першою моєю думкою було те, що він Tповинен бути абсолютно завершеним. Радий, що перевірив стандарт.
AndyG
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.