C ++类模板构造函数 - 带数组(U *)的重载引用(U&)失败

时间:2016-04-24 07:14:30

标签: c++ arrays templates constructor constructor-overloading

我正在尝试构建一个构造函数,将一个数组作为一个参数,重载另一个采用标量的人。代码如下。

#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。该程序运行正常。我该怎么做才能纠正这个问题?

任何建议都表示赞赏!

3 个答案:

答案 0 :(得分:3)

以下是使用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

答案 1 :(得分:2)

  

我正在尝试构建一个构造函数来将数组作为参数

(...)

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

您不接受数组。你可以使用指针,它可能指向或不指向数组,即使它指向一个数组,谁说数组至少有三个元素?这是一个严重的设计缺陷。

C ++确实允许你通过引用或const引用获取原始数组,即使语法很糟糕:

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 :(得分:1)

即使两个构造函数都使用显式说明符并尝试避免类型转换,您应该注意第一个与第二个一样好。如果你用U代替浮动*你会得到:

显式SmallVec(const float *&amp; scalar)

这是完全可以接受的,并将解释编译错误。 您可以通过将第二个构造函数更改为:

来解决此问题
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());