Кажется, всегда есть тонны вопросов относительно контекста создания экземпляров.
Пример, приведенный MSalters, проблематичен:
template <typename T> void foo() {
T() + T();
}
рассмотрите следующий код:
#include <iostream>
using namespace std;
template <typename T> void foo() {
T() + T();
}
class A {};
void operator +(const A&, const A&)
{
cout << "Called operator+(const A&, const A&)" <<endl;
}
int main()
{
foo<A>();
}
Это компилируется и запускается на всех компиляторах, но если вы поместите определение класса A в пространство имен:
#include <iostream>
using namespace std;
template <typename T> void foo() {
T() + T();
}
namespace {
class A {};
}
void operator+(const A&, const A&)
{
cout << "operator+(const N::A&, const N::A&)" << endl;
}
int main()
{
foo<A>();
}
Clang не сможет скомпилироваться, но VC ++ и gcc скомпилируются. Зачем? какой компилятор соответствует спецификации?
Честно говоря, я не знаю. Какой-то компилятор, такой как gcc, даже противоречит сам себе в этой области. Рассмотрим следующий код:
#include <iostream>
using namespace std;
template <typename T> void foo() {
g(T());
}
namespace {
class A {};
}
void g(A a)
{
cout << "g(A)" << endl;
}
int main()
{
foo<A>();
}
Просто измените с "operator +" на функцию с именем "g", gcc не сможет скомпилировать ??? Почему ???
Если спецификация верна, то почему GCC не может найти 'g'?
6. Контекст экземпляра выражения, который зависит от аргументов шаблона, представляет собой набор объявлений с внешней связью
объявлено до момента создания шаблона
специализация в той же переводческой единице.
Когда я читал Бьярна Страуструпа "Язык программирования C ++, 4-е издание", 26.3.5 Шаблоны и пространства имен, у него был такой пример:
namespace N{
class A{};
char f(A);
}
char f(int);
template<typename T>
char g(T t)
{
return f(t); //choose f() depending on what T is
}
char f(double);
char c1 = g(N::A()); //causes N::f(N::A) to be called
char c2 = g(2); //causes f(int) to be called
char c3 = g(2.1); //causes f(int) to be called, f(double) not considered
Здесь f (t) явно зависит, поэтому мы не можем связать f в точке определения. Чтобы сформировать специализацию для g (N :: A), компилятор ищет в пространстве имен N функции с именем f () и ребра N :: f (N :: A).
Значение f (int) найдено, поскольку оно находится в области видимости в точке определения шаблона. F (double) не найден, потому что он не находится в области видимости в точке определения шаблона, и зависимый от аргумента поиск не находит, что глобальная функция принимает только аргументы встроенных типов.
Так что это беспорядок!