Конструктор шаблона класса C ++ — ссылка на перегрузку (U & amp;) с массивом (U *) завершилась неудачно

Я пытаюсь построить конструктор, который будет принимать массив в качестве аргумента, который перегружает другого, который вместо этого принимает скаляр. Код ниже.

#include <iostream>

template <typename T>
class SmallVec { // This is a 3 dimensional vector class template
public:
T data[3] = {0}; // internal data of class
template <typename U>
explicit SmallVec(const U& scalar) { // if a scalar, copy it to each element in data
for(auto &item : data) {
item = static_cast<T>(scalar);
}
}
template <typename U>
explicit SmallVec(const U* vec) { // if a vector, copy one by one
for(auto &item : data) {
item = static_cast<T>(*vec);
vec++;
}
}
};

int main() {
float num = 1.2;
float *arr = new float[3];
arr[2] = 3.4;
SmallVec<float> vec1(num); // take num, which works fine
SmallVec<float> vec2(arr); // !!!--- error happens this line ---!!!
std::cout << vec1.data[2] << " "  << vec2.data[2] << std::endl;
return 0;
}

Компилятор жалуется, что

error: invalid static_cast from type 'float* const' to type 'float'

Очевидно, что vec2(arr) по-прежнему вызывает первый конструктор. Однако, если я удалю template <typename U> и заменить U в T, Программа просто отлично работает. Что я должен сделать, чтобы исправить это?

Любые предложения приветствуются!

5

Решение

Я пытаюсь построить конструктор, чтобы принять массив в качестве аргумента

(…)

explicit SmallVec(const U* vec) { // if a vector, copy one by one

Вы не берете массив. Ты взял указатель, который может указывать или не указывать на массив, и даже если он указывает на массив, кто говорит, что в массиве есть как минимум три элемента? Это серьезный недостаток дизайна.

C ++ позволяет вам брать необработанные массивы по ссылке или константной ссылке, даже если синтаксис ужасен:

explicit SmallVec(const U (&vec)[3]) {

Реализация конструктора также отличается:

    for(int index = 0; index < 3; ++index) {
data[index] = static_cast<T>(vec[index]);
}

Смотря на mainОднако проблема идет глубже. Ты используешь new[] выделить массив динамически. Это уже очень плохая идея. По совпадению, ваш пример также пропускает delete[], Почему бы вам не использовать вместо этого локальный массив?

 float arr[3];

Это сделает вашу программу компилируемой и, вероятно, будет работать правильно, но в вашем коде все еще остается неопределенное поведение, потому что вы устанавливаете только 3-й элемент массива на допустимое значение; два других элемента остаются неинициализированными, а чтение из неинициализированного float, даже если вы просто скопируете его, формально это приведет к неопределенному поведению.

Так что лучше сделай это:

 float arr[3] = { 0.0, 0.0, 3.4 };

В дополнение к этому, C ++ 11 предлагает вам использовать std::array, что, как правило, делает вещи немного безопаснее и улучшает синтаксис. Вот полный пример:

#include <iostream>
#include <array>

template <typename T>
class SmallVec { // This is a 3 dimensional vector class template
public:
std::array<T, 3> data; // internal data of class
template <typename U>
explicit SmallVec(const U& scalar) { // if a scalar, copy it to each element in data
for(auto &item : data) {
item = static_cast<T>(scalar);
}
}
template <typename U>
explicit SmallVec(std::array<U, 3> const& vec) { // if a vector, copy one by one
for(int index = 0; index < 3; ++index) {
data[index] = static_cast<T>(vec[index]);
}
}
};

int main() {
float num = 1.2;
std::array<float, 3> arr = { 0.0, 0.0, 3.4 };
SmallVec<float> vec1(num);
SmallVec<float> vec2(arr);
std::cout << vec1.data[2] << " "  << vec2.data[2] << std::endl;
return 0;
}
2

Другие решения

Вот как использовать SFINAE, чтобы получить то, что вы хотите:

#include <vector>
#include <map>
#include <string>

using namespace std;

template<class T>
struct Foo {

template <class U, typename enable_if<is_pointer<U>::value, int>::type = 0>
Foo(U u){}

template <class U, typename enable_if<!is_pointer<U>::value, int>::type = 0>
Foo(U u){}

};int main()
{
Foo<int> f('a'); // calls second constructor
Foo<int> f2("a"); // calls first constructor
}

жить: https://godbolt.org/g/ZPcb5T

2

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

явный SmallVec (const float *& скаляр)

что вполне приемлемо и объяснит ошибку компиляции.
Вы можете решить проблему, изменив второй конструктор на:

template <typename U>
explicit SmallVec(U* const vec) { // if a vector, copy one by one
U* local = vec;
for(auto &item : data) {
item = static_cast<T>(*local);
local++;
}
}

Тем не менее, я предлагаю еще более явный способ:

class ScalarCopy {};
class VectorCopy {};

...

template <typename U>
SmallVec(const U& vec, ScalarCopy);

template <typename U>
SmallVec(const U* const vec, VectorCopy);

и сделать явные звонки:

SmallVec<float> vec1(num, ScalarCopy());
SmallVec<float> vec2(arr, VectorCopy());
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector