0

I'm having a bizarre error, where the compile accepts the code, but does an implicit construction of a type. Previously this wouldn't compile until I added a constructor for the Value class. Here is an example of how my code is set up, but this fails to compile as expected.

#include <iostream>
#include <cassert>


enum class Type {
    Null = 0
    ,Char
    ,Int32
    //etc
};


// My variant class.
class Value {
public:
// I also have templated constructors for Value...
    Value( Type type, void* data = nullptr ) {
        //construct type from data.
        assert( false );
    }
    friend std::ostream& operator<<( std::ostream& os, const Value& v);
};

// If the insertion operator is defined globally, not within the class, then the compiler
// will do the implicit conversion.
std::ostream& operator<<( std::ostream& os, const Value& v) {
    return os << "Printing Value";
}

// In my code, what this really does is get the type from the lua stack
// and converts the type to my own. I'll just do a c style cast here.
Type getType( int i ) { return (Type)i; }


int main() {
    // In my code, this statement compiles, but implicitly constructs Value 
    // from the return type "Type".
    std::cout << getType( 2 ) << "\n";
    return 0;
}

Has anyone ran into this problem before? Are there any language features that would cause this to take place? What types of things should I look for to prevent this from happening?(I know I can just change the constructor to require the "data" parameter, but I'm looking for the root cause)

Edit: I figured out what allows the compiler to do the implicit conversion, see below.

h4tch
  • 264
  • 1
  • 8
  • 3
    I'm confused about what you're expecting the behavior here to be. Can you provide a [short, self-contained, correct, compilable example](http://sscce.org/) that demonstrates your problem? – Adam Rosenfield Feb 06 '14 at 19:21
  • 1
    See if this answers your question: http://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-in-c-mean – m24p Feb 06 '14 at 19:21
  • @m24p Use of the "explicit" keyword works perfectly. I can now prevent it from compiling. – h4tch Feb 06 '14 at 19:31
  • @Adam I couldn't provide an example of something that compiles because I couldn't and still can't figure out why the code compiles. I was just showing the case that should fail, but wasn't for me. – h4tch Feb 06 '14 at 19:31
  • I'm glad my comment added something useful to the post (hint hint). C++ is allowed to do implicit conversion if you don't explicitly disallow it. That may include constructing something you didn't explicitly ask to be constructed. Thus, the explicit keyword. Keep in mind C++ was making an object-oriented language but with backwards compatibility with C. So some of the behavior that seems odd is going to be because they wanted to be C-like. – m24p Feb 06 '14 at 19:37
  • It's weird because I could spend all day trying to get the example to compile without explicitly doing the conversion, and I would probably fail at coercing the compiler to do the implicit conversion. – h4tch Feb 06 '14 at 19:41
  • Could your real code be compiled in C++03 mode? Does it really use `enum class`? – pmr Feb 06 '14 at 19:42
  • Yes, it uses enum class. My code heavily depends on C++11. – h4tch Feb 06 '14 at 19:47
  • To explain why the code you show does not compile: when looking for `operator<<` the best match is the instantiation of this `operator<<(basic_ostream&&, const T&)`. It will error, because you cannot bind `std::cout` to `basic_ostream&&` and no conversion will take place. Does your original code possibly contain any templated `operator<<`? – pmr Feb 06 '14 at 19:51
  • @pmr It's hard to say for sure. I found one templated operator<<, but it's argument takes a vector. Perhaps that's what's happening. – h4tch Feb 06 '14 at 20:00
  • That sounds unlikely, but this question is impossible to answer unless you manage to reproduce the problem. The answer you have accepted provides a working solution, but it's reasoning is very poor. – pmr Feb 06 '14 at 20:06
  • I just figured it out. – h4tch Feb 06 '14 at 20:11

1 Answers1

2

Your class has a conversion constructor that converts an object of type Type to an object of type Value

Value( Type type, void* data = nullptr ) {
    //construct type from data.
    assert( false );
}

So then you write

std::cout << getType( 2 ) << "\n";

operator << is not overloaded for type Type. So the compiler tries to convert implicitly the operand of the operator to type that can be used with the operator. And it finds such conversion because you have the conversion constructor.

You should declare the constructor as

explicit Value( Type type, void* data = nullptr );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • In the code above, i got the idea to add operator<< to add Value, but it still doesn't compile. So, is this a "feature" of C++ then, to do constructions of types you're not trying to construct, but only when the compile feels like it? Awesome. – h4tch Feb 06 '14 at 19:35
  • 1
    Yup. Until you get into the habit of putting `explicit` on every constructor that might take one argument unless you think of a reason not to. – aschepler Feb 06 '14 at 19:40
  • This explanation is wrong. The code provided by OP does not compile for very good reasons. – pmr Feb 06 '14 at 19:43
  • I used to put it on everything even though I didn't know its purpose, but of course that didn't last. – h4tch Feb 06 '14 at 19:44