Is relying on implicit argument conversion considered dangerous?

C++ has a feature (I cannot figure out the proper name of it), that automatically calls matching constructors of parameter types if the argument types are not the expected ones.

A very basic example of this is calling a function that expects a std::string with a const char* argument. The compiler will automatically generate code to invoke the appropriate std::string constructor.

I’m wondering, is it as bad for readability as I think it is?

Here’s an example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class Texture {
public:
Texture(const std::string& imageFile);
};
class Renderer {
public:
void Draw(const Texture& texture);
};
Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);
</code>
<code>class Texture { public: Texture(const std::string& imageFile); }; class Renderer { public: void Draw(const Texture& texture); }; Renderer renderer; std::string path = "foo.png"; renderer.Draw(path); </code>
class Texture {
public:
    Texture(const std::string& imageFile);
};

class Renderer {
public:
    void Draw(const Texture& texture);
};

Renderer renderer;
std::string path = "foo.png";
renderer.Draw(path);

Is that just fine? Or does it go too far? If I shouldn’t do it, can I somehow make Clang or GCC warn about it?

3

This is referred to as a converting constructor (or sometimes implicit constructor or implicit conversion).

I’m not aware of a compile-time switch to warn when this occurs, but it’s very easy to prevent; just use the explicit keyword.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>class Texture {
public:
explicit Texture(const std::string& imageFile);
};
</code>
<code>class Texture { public: explicit Texture(const std::string& imageFile); }; </code>
class Texture {
public:
    explicit Texture(const std::string& imageFile);
};

As to whether or not converting constructors are a good idea: It depends.

Circumstances in which implicit conversion makes sense:

  • The class is cheap enough to construct that you don’t care if it’s implicitly constructed.
  • Some classes are conceptually similar to their arguments (such as std::string reflecting the same concept as the const char * it can implicitly convert from), so implicit conversion makes sense.
  • Some classes become a lot more unpleasant to use if implicit conversion is disabled. (Think of having to explicitly invoke std::string every time you want to pass a string literal. Parts of Boost are similar.)

Circumstances in which implicit conversion makes less sense:

  • Construction is expensive (such as your Texture example, which requires loading and parsing a graphic file).
  • Classes are conceptually very dissimilar to their arguments. Consider, for example, an array-like container that takes its size as an argument:
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class FlagList
{
FlagList(int initial_size);
};
void SetFlags(const FlagList& flag_list);
int main() {
// Now this compiles, even though it's not at all obvious
// what it's doing.
SetFlags(42);
}
class FlagList { FlagList(int initial_size); }; void SetFlags(const FlagList& flag_list); int main() { // Now this compiles, even though it's not at all obvious // what it's doing. SetFlags(42); }
    class FlagList
    {
        FlagList(int initial_size); 
    };

    void SetFlags(const FlagList& flag_list);

    int main() {
        // Now this compiles, even though it's not at all obvious
        // what it's doing.
        SetFlags(42);
    }
  • Construction may have unwanted side effects. For example, an AnsiString class should not implicitly construct from a UnicodeString, since the Unicode-to-ANSI conversion may lose information.

Further reading:

  • The C++ FAQ on explicit constructors
  • The Google C++ Style Guide says to nearly always use explicit constructors.
  • This StackOverflow question goes into more pros and cons.

This is more of a comment than an answer but too big to put in a comment.

Interestingly, g++ does not let me do that:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>#include <iostream>
#include <string>
class Texture {
public:
Texture(const std::string& imageFile)
{
std::cout << "Texture()" << std::endl;
}
};
class Renderer {
public:
void Draw(const Texture& texture)
{
std::cout << "Renderer.Draw()" << std::endl;
}
};
int main(int argc, char* argv[])
{
Renderer renderer;
renderer.Draw("foo.png");
return 0;
}
</code>
<code>#include <iostream> #include <string> class Texture { public: Texture(const std::string& imageFile) { std::cout << "Texture()" << std::endl; } }; class Renderer { public: void Draw(const Texture& texture) { std::cout << "Renderer.Draw()" << std::endl; } }; int main(int argc, char* argv[]) { Renderer renderer; renderer.Draw("foo.png"); return 0; } </code>
#include <iostream>
#include <string>

class Texture {
        public:
                Texture(const std::string& imageFile)
                {
                        std::cout << "Texture()" << std::endl;
                }
};

class Renderer {
        public:
                void Draw(const Texture& texture)
                {
                        std::cout << "Renderer.Draw()" << std::endl;
                }
};

int main(int argc, char* argv[])
{
        Renderer renderer;
        renderer.Draw("foo.png");

        return 0;
}

Produces the following:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>$ g++ -o Conversion.exe Conversion.cpp
Conversion.cpp: In function ‘int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to ‘Renderer::Draw(const char [8])
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)
</code>
<code>$ g++ -o Conversion.exe Conversion.cpp Conversion.cpp: In function ‘int main(int, char**)’: Conversion.cpp:23:25: error: no matching function for call to ‘Renderer::Draw(const char [8])’ Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&) </code>
$ g++ -o Conversion.exe Conversion.cpp 
Conversion.cpp: In function ‘int main(int, char**)’:
Conversion.cpp:23:25: error: no matching function for call to ‘Renderer::Draw(const char [8])’
Conversion.cpp:14:8: note: candidate is: void Renderer::Draw(const Texture&)

However, if I change the line to:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> renderer.Draw(std::string("foo.png"));
</code>
<code> renderer.Draw(std::string("foo.png")); </code>
   renderer.Draw(std::string("foo.png"));

It will perform that conversion.

4

It’s called implicit type conversion. In general it’s a good thing, as it inhibits unnecessary repetition. For example, you automatically get a std::string version of Draw without having to write any extra code for it. It can also aid in following the open-closed principle, as it lets you extend Renderer‘s capabilities without modifying Renderer itself.

On the other hand, it’s not without drawbacks. It can make it difficult to figure out where an argument is coming from, for one thing. It can sometimes produce unexpected results in other cases. That’s what the explicit keyword is for. If you put it on the Texture constructor it disables using that constructor for implicit type conversion. I’m not aware of a method to globally warn on implicit type conversion, but that doesn’t mean a method doesn’t exist, only that gcc has an incomprehensibly large number of options.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật