简体   繁体   中英

Why doesn't the compiler generate compile errors if an incorrect argument type is passed to a struct initialiser list?

I have defined a struct, which has a constructor:

struct MyStruct
{
    MyStruct(const int value)
        : value(value)
    {
    }
    int value;
};

and the following objects:

int main()
{
    MyStruct a (true);
    MyStruct b {true};
}

But I haven't received any compile errors, either with MVS2015 or Xcode 7.3.1.

  1. Why am I not getting any compile errors?
  2. How do I make the compiler help me detect this? (Initially, the struct was written to have bool data, but after some time, code changed and bool became int and several bugs were introduced.)

A bool can be implicitly converted to an int in a way that's value preserving. The only disallowed conversions with brace initialization are narrowing conversions (eg the reverse bool{42} ).

If you want to ensure that your class is constructible only with int , then the direct way is simply to delete all the other constructors:

struct MyStruct
{
    explicit MyStruct(int i) : value(i) { }

    template <typename T>
    MyStruct(T t) = delete;

    int value;
};

Here, MyStruct{true} and MyStruct(false) will yield calls to MyStruct::MyStruct<bool> , which is defined as deleted and hence is ill-formed.

The advantage of this over static_assert is that all the type traits will actually yield the correct values. For instance, std::is_constructible<MyStruct, bool> is std::false_type .

Here's a construction that allows you to only initialize your class from an int value:

#include <type_traits>

struct MyStruct
{
    template <typename T>
    MyStruct(T t) : value(t)
    {
        static_assert(std::is_same<T, int>::value, "Bad!");
    }

    int value;
};

That's because the template argument deduction required by this constructor template will produce the exact type of the argument and not perform conversions, so you can perform tests on that type.

You should perhaps also or instead use SFINAE to constrain the constructor, so that MyStruct doesn't present itself as constructible from anything.

Furthermore, you should probably also make the constructor template explicit so that random integers don't become MyStruct instances.

In other words, I'd write it like so:

struct MyStruct
{
    template <typename T,
              typename = std::enable_if_t<std::is_same<T, int>::value>>
    MyStruct(T t) : value(t) {}

    // ...

The simplest solution is to declare a bool constructor as deleted isn't it?

struct MyStruct
{
    MyStruct(bool) = delete;

    MyStruct(const int value)
    : value(value)
    {
    }
    int value;
};

example error output:

...
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:68:14: error: call to deleted constructor of 'MyStruct'
    MyStruct b {true};
             ^ ~~~~~~
/Users/xxxxxxx/play/fast_return/skeleton/main.cpp:57:9: note: 'MyStruct' has been explicitly marked deleted here
        MyStruct(bool) = delete;
        ^
2 errors generated.

Because a bool can be implicitly converted to an int .

This is a language feature that you cannot turn off, sorry.

值true转换为值1(int)。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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