Re[7]: Какую из перегруженных функций выбрал компилятор?
От: Bell Россия  
Дата: 07.09.06 14:57
Оценка: 65 (4)
Здравствуйте, igna, Вы писали:

I>Там знаешь, что написано? Вот: "Fortunately, C++ defines the POI for a reference to a nonclass specialization to be immediately after the nearest namespace scope declaration or definition that contains that reference." То есть сразу после main и до объявлений обоих возвращающих second функций f.


Я знаю что там написано — иначе бы не приводил ссылку Попробую дать более полный ответ:

14.6.4 Dependent name resolution
1 In resolving dependent names, names from the following sources are considered:
— Declarations that are visible at the point of definition of the template.
— Declarations from namespaces associated with the types of the function arguments 
  both from the instantiation context (14.6.4.1) and from the definition context.


В нашем примере имя f — зависимое (из-за аргуметна, тип которого определяется параметром шаблона g) и в случае, если этот аргумент имеет определенный пользоватетем тип, то во второй фазе рассматриваются имена из всего глобального пространства имен (потому что тип D лежит в глобальном пространстве). Поэтому то, что POI g находится сразу после main, не играет роли.
Вот пример для иллюстрации:

struct B {};
struct D : B {};

struct first {};
struct second {};

//first f(B const&);

template <typename T>
void g(T const& x)
{
    first test=f(x);
}

int main()
{
    g(D());
}

second f(D const&);



"ComeauTest.c", line 12: error: no suitable user-defined conversion from "second" to
"first" exists
first test=f(x);
^
detected during instantiation of "void g(const T &) [with T=D]" at
line 17


Т.е. имя second f(D const&); находится во время второй фазы несмотря на то, что оно невидимо ни в точке определения, ни в точке инстанциирования шаблона, а ошибка связана с невозможностью преобразования типов.

Если же зависимый аргумент имеет встроенный тип, то второй фазы фактически нет, и при компиляции аналогичного примера имя не будет найдено:
struct first {};
struct second {};

//first f(double);

template <typename T>
void g(T const& x)
{
    first test=f(x);
}

int main()
{
    g(int());
}

second f(int);



"ComeauTest.c", line 9: error: identifier "f" is undefined
first test=f(x);
^
detected during instantiation of "void g(const T &) [with T=int]" at
line 14

Любите книгу — источник знаний (с) М.Горький
Re[4]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 09:06
Оценка: 1 (1) +1
Здравствуйте, night beast, Вы писали:

NB>в данном случае можно без sizeof


Спасибо, получилось. Comeau выбирает f(double), как и положено.

Кстати first и second можно определять и так:

struct first {};
struct second {};
Re[3]: Какую из перегруженных функций выбрал компилятор?
От: night beast СССР  
Дата: 07.09.06 08:46
Оценка: 6 (1)
Здравствуйте, igna, Вы писали:

I>Компилировать можно. Comeau C++ Online.


в данном случае можно без sizeof

#include <iostream>

typedef char (&first)[1];
typedef char (&second)[2];

first f(double);

template <typename T>
void g(T const& x)
{
    first test=f(x);
}

int main()
{
    g(1);
    std::cout << '\n';
}

second f(int);



I>Visual C++ 8.0 и gcc 3.4.4 выбирают f(int), не пойму, почему.


я тоже
Re: Какую из перегруженных функций выбрал компилятор?
От: night beast СССР  
Дата: 07.09.06 07:49
Оценка: 2 (1)
Здравствуйте, igna, Вы писали:

I>Как узнать, какую из перегруженных функций выбрал компилятор, если нет возможности ни запустить, ни даже скомпоновать программу?


sizeof(foo(args))? или компилировать тоже нельзя?
Re: Какую из перегруженных функций выбрал компилятор?
От: Кодт Россия  
Дата: 08.09.06 13:55
Оценка: 2 (1)
Здравствуйте, igna, Вы писали:

I>Как узнать, какую из перегруженных функций выбрал компилятор, если нет возможности ни запустить, ни даже скомпоновать программу?


Простой способ состоит в том, чтобы у ожидаемой и остальных сигнатур сделать разные несовместимые типы результатов.
template<bool> struct bool2type {};

bool2type<true> foo(int);
bool2type<false> foo(unsigned);
bool2type<false> foo(double);

int main()
{
    bool2type<true> t = foo( "hello"[2] ); // надеюсь, что будет foo(int)
}
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[6]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 13:01
Оценка: +1
Здравствуйте, Bell, Вы писали:

B>Выглядит это действительно ненормально, но формально Комо прав.

B>Все дело в том, что в твоем примере аргумент в вызове функции g имеет определенный пользователем тип, а в этом случае во время второй фазы поиска имен (two-phase lookup) вносит свой вклад ADL. Поэтому и выбирается second f(D const&). Если же вызвать g с аргуметном встроенного типа, то второй фазы просто не будет, и при вызове будет использована привязка, полученная на первой фазе.
B>Вот иллюстрация этого:

B>struct B {};
B>struct D : B {};

B>struct first {};
B>struct second {};

B>first f(B const&);
B>first f(double);


B>template <typename T>
B>void g(T const& x)
B>{
B>    first test=f(x);
B>}

B>int main()
B>{
B>   g(int());//ok (вызывается f(double))
B>   g(D());//error (вызывается f(const D&))
B>   return 0;
B>}

B>second f(int);
B>second f(D const&);


B>ЗЫ

B>В "C++ Templates: The Complete Guide" это описывается в разделе 10.3.

Там знаешь, что написано? Вот: "Fortunately, C++ defines the POI for a reference to a nonclass specialization to be immediately after the nearest namespace scope declaration or definition that contains that reference." То есть сразу после main и до объявлений обоих возвращающих second функций f.
Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 07:41
Оценка:
Как узнать, какую из перегруженных функций выбрал компилятор, если нет возможности ни запустить, ни даже скомпоновать программу?
Re[2]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 08:18
Оценка:
Здравствуйте, night beast, Вы писали:

NB>sizeof(foo(args))? или компилировать тоже нельзя?


Спасибо, как бы это можно было применить в таком случае:

#include <iostream>

void f(double) { std::cout << "f(double)"; }

template <typename T>
void g(T const& x)
{
    f(x);
}

int main()
{
    g(1);
    std::cout << '\n';
}

void f(int) { std::cout << "f(int)"; }

?

Компилировать можно. Comeau C++ Online.

Visual C++ 8.0 и gcc 3.4.4 выбирают f(int), не пойму, почему.
Re[3]: Какую из перегруженных функций выбрал компилятор?
От: Bell Россия  
Дата: 07.09.06 09:31
Оценка:
Здравствуйте, igna, Вы писали:

I>Спасибо, как бы это можно было применить в таком случае:

I>
I>#include <iostream>

I>void f(double) { std::cout << "f(double)"; }

I>template <typename T>
I>void g(T const& x)
I>{
I>    f(x);
I>}

I>int main()
I>{
I>    g(1);
I>    std::cout << '\n';
I>}

I>void f(int) { std::cout << "f(int)"; }
I>

I>?

I>Компилировать можно. Comeau C++ Online.

I>Visual C++ 8.0 и gcc 3.4.4 выбирают f(int), не пойму, почему.

Комо выбирает f(double) — немного измененный пример компилируется:

#include <iostream>

int f(double) { std::cout << "f(double)"; return 0;}

template <typename T>
void g(T const& x)
{
    f(x);
    int arr[sizeof(f(x))] = {0};
}

int main()
{
    g(1);
    std::cout << '\n';
}

void f(int) { std::cout << "f(int)"; }


VC7 в этом примере действительно выбирает f(int), однако начинает правильно вести себя, если функцию g сделать нешаблонной.

ЗЫ
Мне кажется, что примерно месяц — два назад что-то подобное обсуждалось, но найти к сожалению не могу...
Любите книгу — источник знаний (с) М.Горький
Re[4]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 10:04
Оценка:
Здравствуйте, Bell, Вы писали:

B>Комо выбирает f(double) — немного измененный пример компилируется:


Зато не компилируется вот что:

struct B {};
struct D : B {};

struct first {};
struct second {};

first f(B const&);

template <typename T>
void g(T const& x)
{
    first test=f(x);
}

int main()
{
    g(D());
}

second f(D const&);


При замене first на second внутри g компилируется. Разве это нормально?
Re[5]: Какую из перегруженных функций выбрал компилятор?
От: Bell Россия  
Дата: 07.09.06 12:32
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Bell, Вы писали:


B>>Комо выбирает f(double) — немного измененный пример компилируется:


I>Зато не компилируется вот что:


I>
I>struct B {};
I>struct D : B {};

I>struct first {};
I>struct second {};

I>first f(B const&);

I>template <typename T>
I>void g(T const& x)
I>{
I>    first test=f(x);
I>}

I>int main()
I>{
I>    g(D());
I>}

I>second f(D const&);
I>


I>При замене first на second внутри g компилируется. Разве это нормально?


Выглядит это действительно ненормально, но формально Комо прав.
Все дело в том, что в твоем примере аргумент в вызове функции g имеет определенный пользователем тип, а в этом случае во время второй фазы поиска имен (two-phase lookup) вносит свой вклад ADL. Поэтому и выбирается second f(D const&). Если же вызвать g с аргуметном встроенного типа, то второй фазы просто не будет, и при вызове будет использована привязка, полученная на первой фазе.
Вот иллюстрация этого:

struct B {};
struct D : B {};

struct first {};
struct second {};

first f(B const&);
first f(double);


template <typename T>
void g(T const& x)
{
    first test=f(x);
}

int main()
{
   g(int());//ok (вызывается f(double))
   g(D());//error (вызывается f(const D&))
   return 0;
}

second f(int);
second f(D const&);


ЗЫ
В "C++ Templates: The Complete Guide" это описывается в разделе 10.3.
Любите книгу — источник знаний (с) М.Горький
Re[8]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 07.09.06 16:09
Оценка:
Здравствуйте, Bell, Вы писали:

B>Я знаю что там написано


Сорри, неудачно выразился.


B>14.6.4 Dependent name resolution
B>1 In resolving dependent names, names from the following sources are considered:
B>— Declarations that are visible at the point of definition of the template.
B>— Declarations from namespaces associated with the types of the function arguments 
B>  both from the instantiation context (14.6.4.1) and from the definition context.


Спасибо! Не сообразил, что выделенное можно понять буквально; предполагал, что имеются ввиду декларации, которые были бы видимыми при наличии соответствующей директивы using.
Re[8]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 08.09.06 15:18
Оценка:
Здравствуйте, Bell, Вы писали:

B>... рассматриваются имена из всего глобального пространства имен (потому что тип D лежит в глобальном пространстве).


Интересно бы знать rationale для такого разрешения в случае ADL использовать функцию, объявление которой находится после использования.


B>14.6.4 Dependent name resolution
B>1 In resolving dependent names, names from the following sources are considered:
B>— Declarations that are visible at the point of definition of the template.
B>— Declarations from namespaces associated with the types of the function arguments 
B>  both from the instantiation context (14.6.4.1) and from the definition context.


Зачем здесь последняя строка? Что она означает, что нельзя использовать функцию находящуюся хоть и в одном из namespaces associated with the types of the function arguments, но в другой единице компиляции? Но разве для введения такого ограничения не лучше упомянуть именно единицу компиляции, а не контексты?
Re[9]: Какую из перегруженных функций выбрал компилятор?
От: igna Россия  
Дата: 08.09.06 16:32
Оценка:
Возможно объяснение есть в той же "C++ Templates: The Complete Guide" (10.3.):

A translation unit usually contains multiple POIs for the same instance. For class template instances, only the first POI in each translation unit is retained, and the subsequent ones are ignored (they are not really considered POIs). For nonclass instances, all POIs are retained. In either case, the ODR requires that the instantiations occurring at any of the retained POIs be equivalent, but a C++ compiler does not need to verify and diagnose violations of this rule. This allows a C++ compiler to pick just one nonclass POI to perform the actual instantiation without worrying that another POI might result in a different instantiation.

In practice, most compilers delay the actual instantiation of noninline function templates to the end of the translation unit. This effectively moves the POIs of the corresponding template specializations to the end of the translation unit. The intention of the C++ language designers was for this to be a valid implementation technique, but the standard does not make this clear.

Re[10]: Какую из перегруженных функций выбрал компилятор?
От: elcste  
Дата: 11.09.06 10:20
Оценка:
Здравствуйте, igna, Вы писали:

I>In practice, most compilers delay the actual instantiation of noninline function templates to the end of the translation unit. This effectively moves the POIs of the corresponding template specializations to the end of the translation unit. The intention of the C++ language designers was for this to be a valid implementation technique, but the standard does not make this clear.


Ага, у Страуструпа (C.13.8.4) есть даже такой пример.

As usual, use of global functions can make matters worse. The global namespace is considered the namespace associated with built-in types, so global functions can be used to resolve dependent calls that take built-in types. For example:

    int f(int);

    template<class T> T g(T t) { return f(t); }

    char c = g('a');    // error: alternative resolutions of f(t) are possible

    char f(char);
We could generate the specialization g<char>(char) at the point of instantiation and get f(int) called. Alternatively, we could wait and generate the specialization at the end of the translation unit and get f(char) called. Consequently, the call g('a') is an error.


Но вот где для этого основания в стандарте? Что-то я никак не пойму.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.