LINUX.ORG.RU

Помоги компилятору выбрать правильный вариант!

 , ,


1

6

Можно как-то намекнуть компилятору, что нужно использовать вторую функцию если темплейтоаргумент указан?

#include <iostream>

using namespace std;

template <int Size, typename T>
int getArrayLength(T (&s)[Size]) {
	return Size;
}

template<int Size, typename T>
int getArrayLength(T* s) {
	return Size;
}

int main() {
	int a[100];
	int* b = new int[50];
	
	cout << getArrayLength(a) << endl; // 100 -- первая функция
	cout << getArrayLength<20>(a) << endl; // 20 -- вторая функция
	cout << getArrayLength<50>(b) << endl; // 50 -- вторая функция
	cout << getArrayLength<100>(a) << endl; // ошибка -- муки выбора
	
	return 0;
}

★★★

cout << getArrayLength<100>(a) << endl; // ошибка — муки выбора

Где ошибка? У меня gcc-4.7.3 собирает и для последнего случая использует первую функу.

andreyu ★★★★★
()
Ответ на: комментарий от andreyu

http://ideone.com/lxGq2O

prog.cpp: In function ‘int main()’:
prog.cpp:22:31: error: call of overloaded ‘getArrayLength(int [100])’ is ambiguous
  cout << getArrayLength<100>(a) << endl; // ошибка -- муки выбора
                               ^
prog.cpp:22:31: note: candidates are:
prog.cpp:6:5: note: int getArrayLength(T (&)[Size]) [with int Size = 100; T = int]
 int getArrayLength(T (&s)[Size]) {
     ^
prog.cpp:11:5: note: int getArrayLength(T*) [with int Size = 100; T = int]
 int getArrayLength(T* s) {
Kuzy ★★★
() автор топика

Добавь const:

#include <iostream>

using namespace std;

template <int Size, typename T>
int getArrayLength(const T (&s)[Size]) {
    return Size;
}

template<int Size, typename T>
int getArrayLength(const T* s) {
    return Size;
}



int main() {
    int a[100];
    int* b = new int[50];
    
    cout << getArrayLength(a) << endl; // 100 -- первая функция
    cout << getArrayLength<20>(a) << endl; // 20 -- вторая функция
    cout << getArrayLength<50>(b) << endl; // 50 -- вторая функция
    cout << getArrayLength<100>(a) << endl; // ошибка -- муки выбора
    
    return 0;
}
Starting: /home/valentine/projects/test2/build/test2
100
20
50
100
*** Exited normally ***
Pavval ★★★★★
()
Ответ на: комментарий от Kuzy

О_О работает, но почему?

Затрудняюсь 100% ответить, но я бы сказал, что дело в том, что неконстантный указатель на константный массив vs полностью константный массив - второе имеет приоритет как более жесткое.

Pavval ★★★★★
()
Ответ на: комментарий от uber_cat

gcc 4.8.2 и clang 3.3 ругаются

Проверил на clang 3.4 - без const ругается, с const все пучком.

andreyu ★★★★★
()
Ответ на: комментарий от Kuzy

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

template <int Size, typename T>
int getArrayLength(T (&s)[Size])
{
    cout << "1st" << endl;
    return Size;
}


template<int Size, typename T>
int getArrayLength(const T* s)
{
    cout << "2nd" << endl;
    return Size;
}

auriga% g++-4.8 test.cpp -o test
auriga% ./test
1st
100
uber_cat
()
Ответ на: комментарий от uber_cat

что компилятор не отдает предпочтение константному указателю на тип вместо неконстантного, ибо:

Не константному указателю, а указателю на константу.

Курнул overload resolution. Итого:

1. на top-level const все пофигу (т.е. int* a и int* const a - одно и то же). Это identity conversion, т.е. отсутствие преобразования типа.

2. А вот int* a -> const int* a - это qualification conversion, и она имеет вес. В отличие от нее, прямое сопоставление массива - это identity conversion и оно выигрывает у qualification conversion.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Кстати потому const не всегда работает:

#include <iostream>

using namespace std;

template <int Size, typename T>
int getArrayLength(const T (&s)[Size]) {
    return Size;
}

template<int Size, typename T>
int getArrayLength(const T* s) {
    return Size;
}



int main() {
    const int a[100];
    int* b = new int[50];
    
    cout << getArrayLength<100>(a) << endl;
    
    return 0;
}

Тут a - константный (да, я забил его инициализировать) и оно опять не компилится.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Не константному указателю, а указателю на константу.

да, ошибся.

Курнул overload resolution.

подозревал что дело в cv-квалифиакторах. пойду почитаю, спасибо.

uber_cat
()
Ответ на: комментарий от uber_cat

Конкретно про const - это 13.3.3.2.3. Это не rank, а отдельно прописаная замутка.

ТСу нужно думать в сторону какого-то замута в духе Александреску, чтобы отделить указатель от массива.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval
#include <iostream>

using namespace std;

template <typename T>
class is_ptr
{
};

template <typename T>
class is_ptr<T*>
{
public:
    enum { good };
};

template <int Size, typename T>
int getArrayLength(T (&s)[Size]) 
{
    return Size;
}

template<int Size, typename T>
int getArrayLength(T s) 
{
    is_ptr<T>::good;
    return Size;
}



int main() {
    const int a[100] = {0};
    int* b = new int[50];
    
    cout << getArrayLength(a) << endl;
    cout << getArrayLength<20>(a) << endl;
    cout << getArrayLength<50>(b) << endl;
    cout << getArrayLength<100>(a) << endl;
    
    return 0;
}

Так вроде работает. Еще можно заюзать стандартные type_traits, но IMHO будет более многословно.

Pavval ★★★★★
()
Последнее исправление: Pavval (всего исправлений: 1)
template <typename T>
constexpr typename std::enable_if<std::is_array<T>::value, unsigned int>::type getArrayLength(const T&) {
	return std::extent<T>::value;
}

template<unsigned int Size, typename T>
constexpr unsigned int getArrayLength(T*) {
	return Size;
}

int main() {
	const int a[20] = {0};
	cout << getArrayLength(a); // 20
	cout << getArrayLength<100>(a); // 100
	return 0;
}
vzzo ★★★
()
Последнее исправление: vzzo (всего исправлений: 1)
Ответ на: комментарий от vzzo

Без с++11

template <typename T, unsigned int Size>
unsigned int getArrayLength(const T (&)[Size]) {
	return Size;
}

template<unsigned int Size, typename T>
unsigned int getArrayLength(const T*) {
	return Size;
}

int main() {
	const int a[20] = {0};
	cout << getArrayLength(a); // 20
	cout << getArrayLength<100>(a); // 100
	return 0;
}

vzzo ★★★
()
Ответ на: комментарий от vzzo

Что не будет? Работает. И да - я не задумывал SFINAE. Я использую проверку чисто для обрыва компиляции, если передают не массив и не указатель. Если такая обработка ошибок не нужна, то is_ptr<T>::good вообще убирается и все отлично работает.

Pavval ★★★★★
()
Ответ на: комментарий от Pavval

Вот так сделал, вроде все работает.

#include <iostream>
#include <type_traits>

using namespace std;

template <int Size, typename T>
int getArrayLength(T (&s)[Size]) {
	cout << 1 << endl;
	return Size;
}

template<int Size, typename T>
int getArrayLength(T s) {
	static_assert(is_pointer<T>::value, "getArrayLength -- first argument should be a pointer");
	cout << 2 << endl;
	return Size;
}

int main() {
	int a[10] = {0};
	const int b[10] = {0};
	
	int* c = new int[10];
	const int* d = new int[10];
	
	getArrayLength(a); // 1
	getArrayLength(b); // 1
	
	getArrayLength<10>(a); // 1
	getArrayLength<10>(b); // 1
	
	getArrayLength<5>(a); // 2
	getArrayLength<5>(b); // 2
	
	getArrayLength<10>(c); // 2
	getArrayLength<10>(d); // 2
	
	return 0;
}
Kuzy ★★★
() автор топика

Насколько помню, при decey-и если передается не ссылка, то это сводится к указателю на тип, инача передается как массив

Boy_from_Jungle ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.