Can I override implicit initialization to 0?

Is it possible to write a class such that they are valid:

Foo a; Foo b = 0; Foo c = b; Foo d(0); Foo e(1); Foo f = Foo(1); 

But this is not so:

 int x; Foo a = x; Foo b = 1; Foo c = 2; //etc 

Essentially, my rule is: "Constant 0 implicitly converted to Foo , but no other value is"

+8
c ++ constructor explicit-constructor initializer
source share
4 answers

If you don't mind working Foo b = nullptr; It's pretty easy to hack. Have an explicit constructor from int and implicit from std::nullptr_t .

If you do not mind that you are working, I am not sure that this is possible. The only way to distinguish between literal 0 and other integer literals is to first implicitly convert to pointers and nullptr_t . Therefore, nullptr prefers the nullptr_t parameter to the pointer parameter, therefore, with both constructors, you can filter out the nullptr arguments. However, converting 0 to pointers and nullptr_t have the same rank, so this will lead to the loss of arguments 0 with ambiguity.

Hmm ... something like this might work:

 class Foo { struct dummy; public: explicit Foo(int); // the version that allows Foo x(1); Foo(dummy*); // the version that allows Foo x = 0; template <typename T, typename = typename std::enable_if< std::is_same<T, std::nullptr_t>::value>::type> Foo(T) = delete; // the version that prevents Foo x = nullptr; }; 

I have not really tried this. Theoretically, a template should only participate in overload resolution when the argument is nullptr , because otherwise SFINAE kills it. In this case, however, it should be better than a pointer constructor.

+5
source share
 Foo e(1); 

Calling the implicit constructor Foo, which takes an int as an argument. Essentially this line will do the same, trying to convert int to Foo using this int constructor.

 Foo b = 1; 

You cannot forbid certain values โ€‹โ€‹of this int to be processed directly. If you have your own explicit constructor, you also cannot write the next line.

 Foo b = 0; 

gx_ correctly pointed out that 0 can be converted to std :: nullptr_t. The following actions will apply to your intentions.

 Foo(std::nullptr_t x) : member(0) { } explicit Foo(int c) : member(c) { } // ... Foo a = 0; // compiles Foo b = 1; // doesn't compile // Note: int do_stuff (void) { return 0; } Foo c = do_stuff(); // doesn't compile 
0
source share

One of my ideas:

 Foo(const uint32_t c) : member(0) { static_assert(c == 0, "Nope"); } explicit Foo(uint32_t c) : member(c) { } 

Does it behave sensibly?

-one
source share

I admit that I have not yet fully mastered the rvalue semantics in C ++ 11, but this seems to do what you want:

 class Foo { public: Foo(int&&) {} }; int main() { Foo a(123); int x = 123; Foo b(x); // error here, line 11 return 0; } 

Result:

prog.cpp: 11: error: cannot bind 'int lvalue to' int &&

Comments are welcome if this code has any reservations that I did not notice, or if you can reassure me that it is not.

-one
source share

All Articles