Вот небольшой эксперимент с вычет типа возврата для функций друзей в классе (используя Clang 3.4 SVN и g ++ 4.8.1 с std=c++1y
в обоих случаях), что не задокументировано в связанном рабочем документе
#include <iostream>
struct A
{
int a_;
friend auto operator==(A const& L, A const& R)
{
return L.a_ == R.a_; // a_ is of type int, so should return bool
}
};
template<class T>
struct B
{
int b_;
friend auto operator==(B const& L, B const& R)
{
return L.b_ == R.b_; // b_ is of type int, so should return bool
}
};
using BI = B<int>;
int main()
{
std::cout << (A{1} == A{2}) << "\n"; // OK for Clang, ERROR for g++
std::cout << (BI{1} == BI{2}) << "\n"; // ERROR for both Clang and g++
}
Вопрос: поддерживается ли автоматический возврат типа возврата для функций-друзей в классе в C ++ 14?
Что касается других ответов: мы имеем дело с n3638 здесь, и как это включено в последние проекты C ++ 1y.
Я использую 9514cc28 из GitHub хранилище комитета, который включает в себя некоторые (незначительные) исправления / изменения в n3638 уже.
N3638 позволяет явно:
struct A {
auto f(); // forward declaration
};
auto A::f() { return 42; }
И, как мы можем сделать вывод из [dcl.spec.auto], где указана эта функция, даже следующие будут допустимы:
struct A {
auto f(); // forward declaration
};
A x;
auto A::f() { return 42; }
int main() { x.f(); }
(но об этом позже)
Это принципиально отличается от любого задний обратный тип или поиск зависимого имени, как auto f();
предварительная декларация, аналогичная struct A;
, Это должно быть завершено позже, до того, как оно будет использовано (до того, как потребуется тип возврата).
Кроме того, проблемы в OP связаны с внутренними ошибками компилятора. Недавняя сборка clang ++ 3.4 trunk 192325 Debug + Asserts не компилируется, так как утверждение не выполняется при анализе строки return L.b_ == R.b_;
, Я не проверял с последней версией g ++ на данный момент.
Является ли пример ОП законным по отношению к n3638?
Это немного хитрое ИМО. (Я всегда имею в виду 9514cc28 в этом разделе.)
6 Программа, которая использует
auto
или жеdecltype(auto)
в контексте, явно не разрешенном в этом разделе, является некорректным.2 Тип заполнителя может появиться с декларатором функции в Децл-спецификатор-сл, Тип Спецификатор-сл, преобразование-функция-идентификатор, или же задний обратный тип, в любом контексте, где такой декларатор действителен.
/ 5 также определяет некоторые контексты, но они здесь не имеют значения.
Следовательно, auto func()
а также auto operator@(..)
как правило, допускается (это следует из состава объявления функции как T D
, где T
имеет форму Децл-спецификатор-сл, а также auto
это Тип Спецификатор).
auto
а такжеdecltype(auto)
типа спецификаторы обозначить тип заполнителя, который будет заменен позже, либо путем вычитания из инициализатора, либо путем явной спецификации с задний обратный тип.
и / 2
Если объявленный тип возврата функции содержит тип заполнителя, тип возврата функции выводится из
return
операторы в теле функции, если таковые имеются.
Хотя это не так эксплицитно разрешить декларацию вроде auto f();
для функции (то есть объявления без определения) из n3638 и [dcl.spec.auto] / 11 ясно, что она предназначена для явного, а не явного запрета.
Пока пример
struct A
{
int a_;
friend auto operator==(A const& L, A const& R);
}
auto operator==(A const& L, A const& R)
{ return L.a_ == R.a_; }
должен быть хорошо сформирован. Интересной частью сейчас является определение функции друга внутри определения A
, то есть
struct A
{
int a_;
friend auto operator==(A const& L, A const& R)
{ return L.a_ == R.a_; } // allowed?
}
На мой взгляд, это разрешено. Чтобы поддержать это, я приведу название поиска. Поиск имени внутри определения функций, определенных в объявлении функции-друга, следует за поиском имени функций-членов в соответствии с [basic.lookup.unqual] / 9. / 8 того же раздела определяет неквалифицированный поиск имен, используемых внутри тел функций-членов. Одним из способов, которым имя может быть объявлено для использования, является то, что оно «должно быть членом класса X
или быть членом базового класса X
(10.2) «. Это позволяет широко известным
struct X
{
void foo() { m = 42; }
int m;
};
Обратите внимание, как m
не заявлено до его использования в foo
, но это член X
,
Из этого я делаю вывод, что даже
struct X
{
auto foo() { return m; }
int m;
}
позволено. Это поддерживается clang ++ 3.4 trunk 192325.
Поиск имени требует интерпретировать эту функцию только после struct
было завершено, также рассмотрим:
struct X
{
auto foo() { return X(); }
X() = delete;
};
Точно так же тело функций-друзей, определенных внутри класса, может быть интерпретировано только после завершения класса.
В частности, как насчет friend auto some_function(B const& L) { return L.b_; }
?
Во-первых, нагнетаемого имя класса B
эквивалентно B<T>
см. [temp.local] / 1. Это относится к текущая реализация ([Temp.dep.type] / 1).
ID-выражение L.b_
относится к член текущей инстанции (/ 4). Это также зависимый член текущего экземпляра — это дополнение сделано после C ++ 11, см. DR1471, и я не знаю, что об этом думать: [temp.dep.expr] / 5 утверждает это ID-выражение является не зависит от типа, и, насколько я вижу, [temp.dep.constexpr] не говорит, что это зависит от значения.
Если имя в L.b_
не зависит, поиск имени будет следовать правилам «обычного поиска имени» согласно [temp.nondep]. Иначе, это будет весело (поиск зависимых имен не очень хорошо указан), но, учитывая, что
template<class T>
struct A
{
int foo() { return m; }
int m;
};
принимается большинством компиляторов, я думаю, что версия с auto
должен быть действительным тоже.
Есть также раздел о друзьях шаблонов в [temp.friend], но IMO, он не проливает свет на поиск имени здесь.
Других решений пока нет …