Разное поведение type traits в конструкторе и методах класса
Вот мой класс в котором конструкторы вызывают методы в качестве инициализатора. Сигнатуры конструкторов и этих методов идентичны (с той лишь разницей, что одни - конструкторы, а другие - методы, которые что-то возвращают)
#include <iostream>
#include <vector>
#include <type_traits>
#include <concepts>
// TMyClass ********************************************************************************
template <size_t Size>
class TMyClass {
public:
using TData = std::vector<size_t>;
private:
TData data;
protected:
template <std::integral size_type>
TData InitData(const size_type(&arr)[Size]) {
std::vector<size_t> tmp(Size);
for (size_t i = 0; i < Size; ++i) {
tmp[i] = arr[i];
}
return tmp;
}
template <std::integral size_type>
TData InitData(const size_type* arr, size_t size = Size) {
if (size != Size) throw;
std::vector<size_t> tmp(Size);
for (size_t i = 0; i < Size; ++i) {
tmp[i] = arr[i];
}
return tmp;
}
public:
template <std::integral size_type>
TMyClass(const size_type(&arr)[Size])
: data(InitData(arr)) // <------------------ ERROR
{}
template <std::integral size_type>
TMyClass(const size_type* arr, size_t size = Size)
: data(InitData(arr, size))
{}
};
// M A I N ********************************************************************************
int main()
{
int arr[3] = { 3, 4, 5 };
TMyClass<3> obj1(arr);
std::vector<int> vec = { 1, 2, 3 };
TMyClass<3> obj2(vec.data()); // <------------------ ?????
std::cout << "\n\n - the end -\n";
std::cout << "press [Enter]...";
std::cin.get();
}
В итоге я получаю ошибку (строка в коде помечена "<------------------ ERROR"):
TMyClass<3>::InitData: неоднозначный вызов перегруженной функции
Казалось бы всё логично и исправление на
template <std::integral size_type>
TData InitData(const size_type* arr, size_t size) {
// ...
}
(убираем значение по умолчанию для аргумента size
) решает вопрос.
НО! Почему тогда эта ошибка не возникает на строке
TMyClass<3> obj2(vec.data()); // <------------------ ?????
Ведь в конструкторе
template <std::integral size_type>
TMyClass(const size_type* arr, size_t size = Size)
: data(InitData(arr, size))
{}
это значение по умолчанию для аргумента size
всё еще есть.
PS: Вопрос не в логике класса: Как правильно/не правильно? Вопрос в том, почему разное поведение в одинаковых (как мне кажется) ситуациях?
PPS: Использование стандартных type_traits
template <typename size_type, std::enable_if_t< std::is_integral_v<size_type>, bool > = true>
даёт тот же результат
PPS: Тестировал на Visual Studio и Qt
Ответы (2 шт):
НО! Почему тогда эта ошибка не возникает на строке
Потому что существует неявное преобразование array-to-pointer, но не наоборот :)
int arr[3];
int (&r1)[3] = arr; // Ok
int* r2 = arr; // Ok
int* ptr;
int (&r3)[3] = ptr; // Ошибка
int* r4 = ptr; // Ок
Вроде разобрался благодаря @Harry и @isnullxbh. Действительно, моё предположение:
Вопрос в том, почему разное поведение в одинаковых (как мне кажется) ситуациях?
было не верным. Я не внимательно отнёсся к приведению типов.
Код, поведение которого соответствует тому, что я от него ожидал:
#include <iostream>
#include <vector>
#include <concepts>
template <typename T>
concept integral_pointer = std::is_pointer_v<T> && std::integral<typename std::remove_pointer<T>::type>;
// TMyClass ********************************************************************************
template <size_t Size>
class TMyClass {
public:
using TData = std::vector<size_t>;
private:
TData data;
protected:
template <std::integral size_type>
TData InitData(const size_type(&arr)[Size]) {
TData tmp(Size);
for (size_t i = 0; i < Size; ++i) {
tmp[i] = arr[i];
}
return tmp;
}
template <integral_pointer size_type_ptr>
TData InitData(size_type_ptr arr, size_t size = Size) {
if (size != Size) throw;
TData tmp(Size);
for (size_t i = 0; i < Size; ++i) {
tmp[i] = arr[i];
}
return tmp;
}
public:
template <std::integral size_type>
TMyClass(const size_type(&arr)[Size])
: data(InitData(arr))
{}
template <integral_pointer size_type_ptr>
TMyClass(size_type_ptr arr, size_t size = Size)
: data(InitData(arr, size))
{}
};
// M A I N ********************************************************************************
int main()
{
int arr[3] = { 3, 4, 5 };
TMyClass<3> obj0(arr);
const int (&ref_arr)[3] = arr;
TMyClass<3> obj1(ref_arr);
int* const ptr = new int[3]{ 1, 2, 3 };
TMyClass<3> obj2(ptr);
int const* const& ref_ptr = ptr;
TMyClass<3> obj3(ref_ptr);
delete[] ptr;
std::cout << "\n\n - the end -\n";
std::cout << "press [Enter]...";
std::cin.get();
}