【问题标题】:template class: ctor against function -> new C++ standard模板类:针对函数的 ctor -> 新 C++ 标准
【发布时间】:2011-02-17 04:08:10
【问题描述】:

在这个问题中:
template; Point<2, double>; Point<3, double>
丹尼斯和迈克尔注意到了不合理的愚蠢实现的构造函数。
他们是对的,我当时没有考虑到这一点。 但是我发现构造函数对于像这样的模板类并没有太大帮助,而是一个函数在这里更方便和安全

namespace point {

template < unsigned int dims, typename T >
struct Point {

    T X[ dims ];

    std::string str() {
        std::stringstream s;
        s << "{";
        for ( int i = 0; i < dims; ++i ) {
            s << " X" << i << ": " << X[ i ] << (( i < dims -1 )? " |": " ");
        }
        s  << "}";
        return s.str();
    }

    Point<dims, int> toint() {
        Point<dims, int> ret;
        std::copy( X, X+dims, ret.X );
        return ret;
    }
};

template < typename T >
Point< 2, T > Create( T X0, T X1 ) {
    Point< 2, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1;
    return ret;
}
template < typename T >
Point< 3, T > Create( T X0, T X1, T X2 ) {
    Point< 3, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2;
    return ret;
}
template < typename T >
Point< 4, T > Create( T X0, T X1, T X2, T X3 ) {
    Point< 4, T > ret;
    ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2; ret.X[ 3 ] = X3;
    return ret;
}
};
int main( void ) {
    using namespace point;
    Point< 2, double > p2d = point::Create( 12.3, 34.5 );
    Point< 3, double > p3d = point::Create( 12.3, 34.5, 56.7 );
    Point< 4, double > p4d = point::Create( 12.3, 34.5, 56.7, 78.9 );
    //Point< 3, double > p1d = point::Create( 12.3, 34.5 ); //no suitable user defined conversion exists

    //Point< 3, int > p1i = p4d.toint(); //no suitable user defined conversion exists
    Point< 2, int > p2i = p2d.toint();
    Point< 3, int > p3i = p3d.toint();
    Point< 4, int > p4i = p4d.toint();

    std::cout << p2d.str() << std::endl;
    std::cout << p3d.str() << std::endl;
    std::cout << p4d.str() << std::endl;
    std::cout << p2i.str() << std::endl;
    std::cout << p3i.str() << std::endl;
    std::cout << p4i.str() << std::endl;

    char c;
    std::cin >> c;
}  

新的 C++ 标准对模板类的 ctor 的这一方面是否有任何新的改进、语言特性或简化?
您如何看待命名空间、结构和创建函数组合的实现?
非常感谢提前
哎呀

【问题讨论】:

    标签: c++ templates function constructor


    【解决方案1】:

    由于数组是公共的,因此可以选择省略构造函数并允许聚合初始化(例如 boost::array&lt;T, N&gt;)。

    Point<2, int> p = {1, 2};
    

    这并不比必须调用创建函数更糟糕。 (create 函数作为实用程序可能仍然很方便。)


    在 C++0x 中,你将能够拥有各种酷炫。例如,使用可变参数模板,在编译时检查它是否使用正确数量的参数调用构造函数。 (下面还可以检查参数...U 是否都是T 类型,有更多元编程的乐趣,但它可能不是绝对必要的。)

    //helper to copy variable amount of arguments into an array
    namespace detail {
    
    template <class T, class U>
    void copy_variadic(T* p, U value)
    {
        *p = value;
    }
    
    template <class T, class First, class ...Rest>
    void copy_variadic(T* p, First var, Rest ...args)
    {
        *p = var;
        copy_variadic(++p, args...);
    }
    } //detail
    
    template < unsigned int dims, typename T >
    struct Point {
    
        T X[ dims ];
    
        Point() : X{}
        {
        }
    
        template <class ...U>
        Point(U... args) {
            static_assert(sizeof...(args) == dims, "Too many or too few arguments to Point constructor");
            detail::copy_variadic(X, args...);
        }
        //...
    };
    

    (实际上,经过一些修改 - 完美转发 - copy_variadic 会很好地添加到我的可变参数模板实用程序集合中,如果有人不来指出更好的方法。)

    【讨论】:

    • 可以直接用C++0x和GCC4.5初始化数组:template&lt;class ...U&gt; Point(U... args):X{move(args)...} { }。这也将自动检查没有提供太多的初始化程序:)
    • @Johannes:谢谢,我从来没有想过。 - 但是,std::move 那里不是错了吗? template&lt;class ...U&gt; Point(U&amp;&amp;... args):X{std::forward&lt;U...&gt;(args)...} {static_assert(sizeof...(args) &gt;= dims, "Too few arguments"); }。但它作为实用程序仍然有用,因为它允许复制到任何输出迭代器?
    • @UncleBens 已经将参数复制到参数中,尽管可以安全地移动它们,但我相信没有理由不采用转发方式。我认为这两种方式最终都会做同样的事情。只是他们进行复制/移动的时间不同。如果您知道所有元素都具有非抛出移动,那么早期复制方式是异常安全的,但这对构造函数并不重要:) 我认为您编写的实用程序确实很有用:)
    • @Johannes:从理论上讲(因为在 Point 的情况下,这一切都无关紧要),如果您用右值调用它,转发是否意味着根本不会发生复制?
    • @Uncle 确实如此。但在这两种情况下,都不会复制副本。右值被移动到参数中,然后移动到数组中。或者,如果编译器忽略了第一步,则只有一次移动到数组中。与转发案例一样。我可以看到对于构造函数(异常安全无关紧要),转发方式的好处是它总是只做一步,无论编译器是否优化。但我听说将临时变量直接构造成按值参数是一种很常见的优化。
    【解决方案2】:

    是的,正如 Michael 在他对您之前的问题的回答中指出的那样,在 C++0x 中,您将能够使用初始化列表将任意数量的参数传递给您的 ctor。在您的情况下,代码如下所示:

    template <int dims, class T>
    class point { 
        T X[dims];
    public:
        point(std::initializer_list<T> const &init) { 
            std::copy(init.begin(), init.begin()+dims, X);
        }
    };
    

    你可以用这样的东西创建一个点对象:

    point<3, double> x{0.0, 0.0, 0.0};
    

    就个人而言,我不确定我是否非常喜欢基本设计。特别是,我宁愿看到X 变成std::vector,并严格从传递的参数列表中确定维数,而不是将其作为模板参数:

    template <class T>
    class point { 
        std::vector<T> X;
    public:
        point(std::initializer_list<T> init) {
            std::copy(init.begin(), init.end(), std::back_inserter(X));
        }
    };
    

    不过,这确实有一些权衡 - 具有不同维数的点仍然是同一类型。例如,它基本上断言将 2D 点分配给 3D 点是合理的,反之亦然。

    【讨论】:

    • 你不应该检查initializer_list 有合适数量的项目吗?另外,为什么不使用std::array? (太糟糕了initializer_list 的实现完全被我的 GCC 版本所破坏。)
    • @UncleBens:是的,您应该检查initializer_list 是否足够大。 std::array 在这里可能也有意义——我只是还不习惯使用它......
    • 我还建议使用可变参数模板。但是你能在编译时检查initializer_list 的大小吗? - 我也认为vector 在这里可能不太适合:希望这些东西轻量级,动态分配和空间开销可能太多......存储 2 或 3 个双精度数/整数。
    • 是的,第一个和第二个(向量)都可以写成template&lt;typename ...T&gt; point(T const&amp;... t):X{ t... } { }。首先,您可以放置​​一个静态断言static_assert(sizeof...(T) &lt;= dims, "too many initializers!")。但坦率地说,我更喜欢初始化列表。它们仍然可以使用point&lt;3, double&gt; x({0, 0, 0});,而可变参数模板不会,并且需要x{0, 0, 0}x(0, 0, 0)
    猜你喜欢
    • 1970-01-01
    • 2010-11-22
    • 2013-12-08
    • 1970-01-01
    • 2019-03-23
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多