数组类,该类将接受带括号的init列表并推断长度 以前已经问过,但我很好奇,看看新的C++标准是否有什么改变。任何当前或未来的标准都是可以接受的

数组类,该类将接受带括号的init列表并推断长度 以前已经问过,但我很好奇,看看新的C++标准是否有什么改变。任何当前或未来的标准都是可以接受的,c++,c++17,initializer-list,template-argument-deduction,c++20,C++,C++17,Initializer List,Template Argument Deduction,C++20,Q:是否可以创建一个数组类,该类可以使用带括号的init列表初始化,而无需手动指定数组长度,元素存储在堆栈上,并且不需要“make_Array”函数 template<class T, size_t N> struct Array { T items[N]; }; Array<int> foo = { 1, 2, 3 }; 接受c数组的构造函数似乎不起作用,因为初始值设定项\u list不会转换为c数组 是发生在intfoo[]={1,2,3}中的带括号的in

Q:是否可以创建一个数组类,该类可以使用带括号的init列表初始化,而无需手动指定数组长度,元素存储在堆栈上,并且不需要“make_Array”函数

template<class T, size_t N>
struct Array
{
    T items[N];
};

Array<int> foo = { 1, 2, 3 };
接受c数组的构造函数似乎不起作用,因为
初始值设定项\u list
不会转换为c数组

是发生在
intfoo[]={1,2,3}中的
带括号的init列表
T[N]
纯编译器魔法,无法在代码中复制


编辑:这个问题的实质是关于上面的确切语法。没有make_数组,没有额外的模板参数,显式项类型,没有双括号,没有动态分配。如果一个平凡的数组需要一组现代的C++愚蠢,仍然无法支持标准语法,那么我认为这只是一个糟糕的工程权衡。

< P>我不喜欢做坏消息的载体,但是我相信在当前的时间(至少在C++ 17),你的问题的答案是“不”。不可能满足您给定的所有要求。也就是说,这里发布的一些解决方案非常接近,但它们都不能满足您的一个或多个需求

我还怀疑,在不久的将来,答案仍然是“不”。并不是说我是先知,但过去几条C++版本的方向似乎意味着<代码> MaxyLaSux/Cuff>解决方案更有可能是什么被添加而不是更直接的语言支持。 解释

请允许我更详细地解释一下原因

首先考虑C++ 17推理指南。我不会详细讨论这些问题,因为其他关于这个问题的答案都对它们进行了适当的讨论。他们确实非常接近你的要求,但似乎在某个方面没有达到要求。(尽管Max 66的答案似乎满足了你的所有要求,除了多余的括号之外。如果他的语法实际上是有效的,你可能想把答案看作“足够接近”。 下面考虑一个多变的模板解决方案。为了自动确定N,您需要一系列重载函数(基本上是一个带单参数的函数,一个带单参数的函数和其他变量模板)。但这实际上相当于某种形式的
make_数组
,因此它也不算数

最后,我能看到的唯一其他选择是基于初始值设定项列表。问题是如何从该列表中确定N。在C++11中,这显然是不可能的,因为对列表大小的唯一访问是
const
,而不是
constepr
。然而,从C++14开始,
size()
方法实际上是
constepr
,因此您认为至少在理论上可以让编译器基于此推断出N。不幸的是,要做到这一点,您需要将N(模板参数)设为类构造函数(初始值设定项列表)中某个值的默认值。我无法确定以目前的语言形式做这件事的任何方法

我认为未来版本如何支持这一点

支持这一点的一种方法是遵循其他语言的示例,添加直接语言支持,将语法与某些类连接起来。但这本质上使一些类变得“特殊”。请考虑SWIFT中的以下行:

let ar = [1, 2, 3, 4]
在本例中,
ar
是类型为
Array
的对象。但这是由直接编译器支持完成的,即“数组”是一个特例。无论您做什么,您都不能编写一个以相同方式执行的MyArray类(除了让MyArray接受数组作为构造选项之外)。当然,C++的扩展是可能的,但C++往往试图避免那些“特殊”的情况。此外,可以提出这样一个论点:

auto ar = make_array(1, 2, 3, 4);
事实上,它比下面更清楚地表示了意图:(建议使用字符“a”来区分这与初始值设定项列表)

另一种方法,更符合当前C++语法,这样做是将N参数添加到IngalIZELSILL模板类中。毕竟,既然

size()
现在是
constexpr
大小必须在编译时知道,那么为什么不将其作为模板参数使用呢?它可以适当地默认,这样就很少需要它,但它允许类(包括标准类,如
std::array
和自定义类,如您所建议的)将
数组中的N绑定到
初始值设定项列表中的
N
。然后你应该能够按照以下几行写一些东西:

template<class T, size_t N>
struct Array 
{
    explicit Array(std::initializer_list<N> il);
}
模板
结构数组
{
显式数组(std::initializer\u list il);
}
当然,诀窍是以一种不会破坏大量现有代码的方式更改此
初始值设定项列表


我怀疑标准委员会不会遵循这两条路径中的任何一条,但更可能会添加实验性的
make_array
方法。我不认为这是个坏主意。我们习惯于在语言的许多其他部分中使用
,那么为什么不在这里呢?

您可以通过使用明确的演绎指南来解决列表中所有类型都相同的问题:

template <class... T>
Array(T&&... t) -> Array<std::common_type_t<T...>, sizeof...(T)>;

Array foo = { 1.0, 2.0, 3.0f }; // Deduces Array<double,3u>
模板
数组(T&…T)->数组;
数组foo={1.0,2.0,3.0f};//推导数组
C++17中的演绎指南几乎可以工作,但是您必须省略type参数,并且所有项必须具有完全相同的类型

Array foo = { 1, 2, 3 }; // Works
Array<int> foo = { 1, 2, 3 }; // Doesn't work
Array foo = { 1.0, 2.0, 3.0f }; //Doesn't work
Array foo2 = { {1.0, 2.0, 3.0f} }; //Doesn't work: incompatible types
不一定

是的,您不能显式显示类型参数,但是您可以根据类型决定它
template <typename T, typename ... Us>
Array(T, Us...) -> Array<T, 1u + sizeof...(Us)>; 
template <typename ... Ts>
Array(Ts...) -> Array<std::common_type_t<Ts...>, sizeof...(Ts)>;
template <typename T, std::size_t N>
Array(T const (&)[N]) -> Array<T, N>;
//    added ---V         V--- added
Array foo1 = { { 1, 2, 3 } }; // Works
Array foo2 = { {1.0, 2.0, 3.0f} }; //Doesn't work: incompatible types
#include <iostream>

template <typename T>
struct Tag { };

template <typename T, size_t N>
struct Array {
    T data_[N];

    template <typename... U>
    Array(Tag<T>, U... u)
      : data_{static_cast<T>(u)...} // cast to shut up narrowing conversions - bad idea??
    {}
};

template <typename T, typename... U>
Array(Tag<T>, U...) -> Array<T, sizeof...(U)>;

int main()
{
    Array a{Tag<double>{}, 1, 2.0f, 3.0};
    for (auto d : a.data_) {
        std::cout << d << '\n';
    }
}
template<typename T>
class ParamPack {
protected:
    std::vector<T> values_;
    size_t size_;
public:
    template<typename... U>
    ParamPack( U... u ) : 
      values_{ static_cast<T>(u)... }, 
      size_( sizeof...(U) ) {}

    template<typename ... U>
    ParamPack( std::initializer_list<std::is_same<T, U...>( U...)> il ) : 
      values_( il ), size_( il.size() ) {}

    std::vector<T>& operator()() { return values_; }

    size_t size() const { return size_; }
};    

template<typename T>
class Array : public ParamPack<T> {
private:
    T* items_;
public:
    template<typename... U>
    Array( U... u ) : ParamPack<T>::ParamPack( u... ) {
        items_ = this->values_.data();
    }

    template<typename... U>
    Array( std::initializer_list<U...> il ) : ParamPack<T>::ParamPack( il ) {
        items_ = this->values_.data();
    }

    T& operator[]( size_t idx ) {
        return items_[idx];
    }

    T operator[]( size_t idx ) const {
        return items_[idx];
    }

    T* data() const { return items_; }
};

int main() {
    try {
        // Parameter Pack Examples:
        // Variadic Constructor { ... }
        std::cout << "ParamPack<T> Examples:\n";
        std::cout << "Using ParamPack<T>'s Variadic Constructor\n";
        ParamPack<int> pp1( 1, 2, 3, 4  );
        std::cout << "Size: " << pp1.size() << " | Elements: ";
        for( auto& v : pp1() ) {
            std::cout << v << " ";
        }
        std::cout << '\n';      

        std::cout << "Using ParamPack<T>'s Variadic Constructor with an Initializer List\n";
        ParamPack<int> pp2( { 5, 6, 7, 8 } );
        std::cout << "Size: " << pp2.size() << " | Elements: ";
        for( auto& v : pp2() ) {
            std::cout << v << " ";
        }
        std::cout << '\n';

        std::cout << "Using ParamPack<T>'s initializer_list constructor\n";
        std::initializer_list<int> il{ 9,10,11,12 };
        ParamPack<int> pp3( il );       
        std::cout << "Size: " << pp3.size() << " | Elements: ";
        for( auto& v : pp3() ) {
            std::cout << v << " ";
        }
        std::cout << "\n\n";    

        // Array Examples:
        std::cout << "Array<T> Examples:\n";
        std::cout << "Using Array<T>'s initializer_list Constructor\n";
        Array<int> arr( il );       
        for( size_t i = 0; i < arr.size(); i++ ) {
            std::cout << arr[i] << " ";
        }
        std::cout << "\n";

        // Using Variadic Constructor
        std::cout << "Using Array<T>'s Variadic Constructor\n";
        Array<int> testA( 9, 8, 7, 6 );
        for( size_t i = 0; i < testA.size(); i++ ) {
            std::cout << testA[i] << " ";
        }
        std::cout << '\n';

        Array<std::string> testB( "Hello", " World" );
        for( size_t i = 0; i < testB.size(); i++ ) {
            std::cout << testB[i] << " ";
        }
        std::cout << "\n\n";

        // Using Constructor w/ Initializer List
        std::cout << "Using Array<T>'s Variadic Constructor with Initializer List\n";
        Array<int> testC( { 105, 210, 420 } );
        for( size_t i = 0; i < testC.size(); i++ ) {
            std::cout << testC[i] << " ";
        }
        std::cout << "\n\n";

        // Using Initializer List with =
        std::cout << "Using Array<T>'s Initializer List with =\n";
        Array<int> a = { 1, 2, 3, 4 };
        for( size_t i = 0; i < a.size(); i++ ) {
            std::cout << a[i] << " ";
        }
        std::cout << '\n';                

        Array<char> b = { 'a', 'b', 'c', 'd' };
        for ( size_t i = 0; i < b.size(); i++ ) {
            std::cout << b[i] << " ";
        }
        std::cout << '\n';

        Array<double> c = { 1.2, 3.4, 4.5, 6.7 };
        for( size_t i = 0; i < c.size(); i++ ) {
            std::cout << c[i] << " ";
        }
        std::cout << "\n\n"; 

        // Using Initializer List directly
        std::cout << "Using Array<T>'s Initalizer List directly\n";
        Array<uint32_t> a1{ 3, 6, 9, 12 };
        for( size_t i = 0; i < a1.size(); i++ ) {
            std::cout << a1[i] << " ";
        }
        std::cout << "\n\n";

        // Using user defined data type
        struct Point {
            int x_, y_;
            Point( int x, int y ) : x_( x ), y_( y ) {}
        };
        Point p1( 1, 2 ), p2( 3, 4 ), p3( 5, 6 );

        // Variadic Constructor
        std::cout << "Using Array<T>'s Variadic Consturctor with user data type\n";
        Array<Point> d1( p1, p2, p3 );
        for( size_t i = 0; i < d1.size(); i++ ) {
            std::cout << "(" << d1[i].x_ << "," << d1[i].y_ << ") ";
        }
        std::cout << '\n';

        // Initializer List Construtor (reversed order)
        std::cout << "Using Array<T>'s Initializer List Constructor with user data type\n";
        Array<Point> d2( { p3, p2, p1 } );
        for( size_t i = 0; i < d2.size(); i++ ) {
            std::cout << "(" << d2[i].x_ << "," << d2[i].y_ << ") ";
        }
        std::cout << '\n';

        // Initializer List Version = {...} p2 first
        std::cout << "Using Array<T>'s  = Initializer List with user data type\n";
        Array<Point> d3 = { p2, p1, p3 };
        for( size_t i = 0; i < d3.size(); i++ ) {
            std::cout << "(" << d3[i].x_ << "," << d3[i].y_ << ") ";
        }
        std::cout << '\n';

        // Initializer List Directly p2 first p1 & p3 swapped
        std::cout << "Using Array<T>'s Initializer List directly with user data type\n";
        Array<Point> d4{ p2, p3, p1 };
        for( size_t i = 0; i < d4.size(); i++ ) {
            std::cout << "(" << d4[i].x_ << "," << d4[i].y_ << ") ";
        }
        std::cout << '\n';  

        std::initializer_list<Point> ilPoints{ p1, p2, p3 };
        std::cout << "Using Array<T>'s initializer_list Constructor with user data type\n";
        Array<Point> d5( ilPoints );
        for( size_t i = 0; i < d5.size(); i++ ) {
            std::cout << "(" << d5[i].x_ << "," << d5[i].y_ << ") ";
        }
        std::cout << "\n\n";    

        // Need a local copy of the vector instead?
        std::cout << "Using Array<T>'s base class's operator()() to retrieve vector\n";
        std::vector<Point> points = d4(); // using operator()()
        for( auto& p : points ) {
            std::cout << "(" << p.x_ << "," << p.y_ << ") ";
        }
        std::cout << '\n';

        // Need a local copy of the pointer instead?
        std::cout << "Using Array<T>'s data() to get the contents of its internal pointer\n";
        Point* pPoint = nullptr;
        pPoint = d4.data();
        for( size_t i = 0; i < d4.size(); i++ ) {
            std::cout << "(" << pPoint[i].x_ << "," << pPoint[i].y_ << ") ";
        }
        std::cout << '\n';

    } catch( const std::runtime_error& e ) {
        std::cerr << e.what() << '\n';
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
ParamPack<T> Examples:
Using ParamPack<T>'s Variadic Constructor
Size: 4 | Elements: 1 2 3 4
Using ParamPack<T>'s Variadic Constructor with an Initializer List
Size: 4 | Elements: 5 6 7 8 
Using ParamPack<T>'s initializer_list Constructor
Size: 4 | Elements: 9 10 11 12

Array<T> Examples:
Using Array<T>'s initializer_list Constructor
9 10 12 12

Using Array<T>'s Variadic Constructor
9 8 7 6
Hello World

Using Array<T>'s Constructor with Initializer List
105 210 420

Using Array<T>'s Initializer List with =
1 2 3 4
a b c d
1.2 3.4 5.6 7.8

Using Array<T>'s Initializer List directly
3 6 9 12

Using Array<T>'s Variadic Constructor with user data type
(1,2) (3,4) (5,6)
Using Array<T>'s Variadic Constructor With Initializer List of user data type
(5,6) (3,4) (1,2)
Using Array<T>'s = Initializer List with user data type
(3,4) (1,2) (5,6)
Using Array<T>'s Initializer List directly with user data type
(3,4) (5,6) (1,2)
Using Array<T>'s initializer_list Constructor with user data type

Using Array<T>'s base class's operator()() to retrieve vector
(3,4) (5,6) (1,2)   
Using Array<T>'s data() to get the contents of its internal pointer
(3,4) (5,6) (1,2)
Array<int> a( 1, 2, 3, 4 ); // Variadic Constructor Okay
Array<int> a( {1,2,3,4} ); // Initializer List Constructor Okay
Array<int> a = { 1, 2, 3, 4 }; // Initializer List Okay
Array<int> a{ 1,2,3,4 }; // Initializer List Okay 
Array a( 1,2,3,4 );
Array a( {1,2,3,4} );
Array a = { 1, 2, 3, 4 }; 
Array a{ 1,2,3,4 };