简体   繁体   English

防止使用括号或大括号进行初始化

[英]Prevent initialization with parentheses or braces

Given a class C , is it possible to prevent construction using parenthesis (eg forbid C c(1); ) or braces (eg forbid C c{1}; )? Given a class C , is it possible to prevent construction using parenthesis (eg forbid C c(1); ) or braces (eg forbid C c{1}; )?


I am interested in the question per se, but since it got asked what is my motivation:我对这个问题本身很感兴趣,但是因为有人问我的动机是什么:

I do not like the following behaviours:我不喜欢以下行为:

#include <array>
struct aggregate{ int x,y; };
int main() {
  aggregate A{1};
  std::array< aggregate, 2 > B{1,2,3};
}

which yields产生

  1. Ax=1 , Ay=0 Ax=1 , Ay=0
  2. B[0].x=1 , B[0].y=2 , B[1].x=3 , B[1].y=0 B[0].x=1 , B[0].y=2 , B[1].x=3 , B[1].y=0

The first one is at least kind of reasonable, the second one not at all.第一个至少有点合理,第二个根本不合理。 I rather would like to have a compile time error for both of them.我宁愿对他们俩都有一个编译时错误。

My goal is to write a container array similiar to (and as efficient as) std::array which prevents such bad constructions.我的目标是编写一个与std::array类似(并且与它一样高效)的容器array ,以防止这种糟糕的结构。

To be constructed like:构造如下:

#include <array>
struct aggregate{ int x,y; };
int main() {
  array< aggregate, 2 > A; // all elements default initialized
  array< aggregate, 2 > B{aggregate{1,2},aggregate{3,4}};
  array< aggregate, 2 > C{{1,2},{3,4}};  // would be nice but not mandatory

  // everything below here should yield compile time errors
  array< aggregate, 2 > D(1,2);
  array< aggregate, 2 > D{1,2};
  array< aggregate, 2 > D({1,2});
  array< aggregate, 2 > D(1,2,3,4);
  array< aggregate, 2 > D{1,2,3,4};
  // etc...

}

To further detalize my motivation (since it seems, my question is not going to be answered): I am not so concerned about the class aggregate given above, but about the class array .为了进一步详细说明我的动机(因为看起来,我的问题不会得到回答):我不太关心上面给出的 class aggregate ,而是关心 class array Thus, it is not an option to remove/add ctors from the class.因此,不能从 class 中删除/添加 ctor。

So far I have the following code: It prevents nearly all of my unwanted initializations, except that it calls unnecessary often a ctor.到目前为止,我有以下代码:它可以防止几乎所有我不需要的初始化,除了它经常调用不必要的 ctor。 So my idea is: In Debug mode I typedef array to the class tmm::array`` in the code below, in Release mode I typedef it to std::array . But the problem arises that所以我的想法是:在调试模式下,我在下面的代码中将array输入到 class tmm::array`` in the code below, in Release mode I typedef it to std::array . But the problem arises that . But the problem arises that tmm::array allows constructs which are not allowed for std::array`. . But the problem arises that tmm::array allows constructs which are not allowed for构造。

#include <array>
#include <iostream>
#include <type_traits>

template <typename T, typename ...Ts>
inline constexpr bool areT_v = std::conjunction_v<std::is_same<T,Ts>...>;

struct CLASS {
    int x, y;
    CLASS() noexcept { 
        std::cout << "default constructed" << std::endl; 
    }
    CLASS(int x_,int y_) noexcept : x(x_), y(y_) { 
        std::cout << "constructed" << std::endl; 
    }
    CLASS( CLASS const & other ) noexcept : x(other.x), y(other.y) {
        std::cout << "copy constructed" << std::endl; 
    }
};

namespace tmm {
    template< typename T, int rows >
    struct array
    {
        template< typename ... L > constexpr inline
        array( L && ... lst ) noexcept : content{ std::forward< L >( lst ) ... } {
            static_assert( sizeof...(L) == rows || sizeof...(L)==0 );
            static_assert( areT_v<T,L...>, "" );
        }
        array() = default;  // not sure whether I need this one
        T content[rows];
    };
}

int main() {
    #define INIT CLASS{1,2},CLASS{3,4},CLASS{5,6}
    std::array<CLASS,3>tb3b {INIT};  // 3 ctors
    tmm::array<CLASS,3> sb3b {INIT};  // 3 ctors and 3 copy ctors
    
//  std::array<CLASS,3> sp3a (INIT);  // does not compile
    tmm::array<CLASS,3>tp3a (INIT);   // compiles
}

Given a class C , is it possible to prevent construction using parenthesis (eg forbid C c(1); ) or braces (eg forbid C c{1}; )? Given a class C , is it possible to prevent construction using parenthesis (eg forbid C c(1); ) or braces (eg forbid C c{1}; )?

To answer your question directly, the object of class C will only be constructible with the constructors you provide.要直接回答您的问题,class C 的C只能使用您提供的构造函数进行构造。 That is, if C does not have a constructor from a single int , it won't be constructible using either C c(1);也就是说,如果C没有来自单个int的构造函数,则无法使用C c(1); or C c{1};C c{1}; syntax.句法。

There are a few caveats, especially given your later explanation.有一些警告,特别是考虑到您后来的解释。 First, if your class C is an aggregate then it follows the standard language rules for initialization (that is, it allows aggregate initialization ).首先,如果您的 class C是一个聚合,那么它遵循标准语言规则进行初始化(也就是说,它允许聚合初始化)。 You cannot have an aggregate class and prohibit its aggregate initialization at the same time.您不能同时拥有聚合 class 并禁止其聚合初始化。 The only way around it is to specify the constructors you want to allow, at which point the class is no longer an aggregate:解决它的唯一方法是指定要允许的构造函数,此时 class 不再是聚合:

struct C
{
    int x,y;

    C() : x(0), y(0) {}
    C(int x, int y) : x(x), y(y) {}
};

C a(10); // error - no constructor from a single int

Next, type conversions may still happen as part of initialization.接下来,类型转换可能仍会作为初始化的一部分发生。 If you also want to prohibit those, you may use deleted constructors or SFINAE .如果您还想禁止这些,您可以使用已删除的构造函数SFINAE For example:例如:

struct C_no_float
{
    int x,y;

    C_no_float() : x(0), y(0) {}
    C_no_float(int x, int y) : x(x), y(y) {}
    C_no_float(double x, double y) = delete;
};

C_no_float a(10, 20); // ok
C_no_float b(1.0, 2.0); // error - calling a deleted constructor

Note that using deleted functions may still be error-prone since the constructor from int s may still be called as a result of type conversion.请注意,使用已删除的函数可能仍然容易出错,因为来自int的构造函数仍可能由于类型转换而被调用。 In other words, this approach does not guarantee that C_no_float is not constructible from anything but exactly a pair of int s.换句话说,这种方法不能保证C_no_float不能从任何东西构造,而只能是一对int Using SFINAE allows for stronger restriction on argument types:使用 SFINAE 可以对参数类型进行更强的限制:

struct C_only_int
{
    int x,y;

    C_only_int() : x(0), y(0) {}
    template< typename T, typename = std::enable_if_t<std::is_same_v<T, int>> >
    C_only_int(T x, T y) : x(x), y(y) {}
};

C_only_int a(10, 20); // ok, T is int
C_only_int b(10l, 20l); // error - T would be long,
                        // template argument substitution fails

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM