Thread: Implications of an explicit constructor taking a std::initializer_list

  1. #1
    C++ Enthusiast M.Richard Tober's Avatar
    Join Date
    May 2011
    Location
    Georgia
    Posts
    56

    Implications of an explicit constructor taking a std::initializer_list

    What are the design considerations of an explicit constructor which takes an initializer list vs a non-explicit constructor (which takes an initializer_list).

    Example:
    Code:
    explicit Foo(std::initializer_list<std::string> il) { ... }
    What effect will making the constructor explicit have? I did some tests and anything that compiled under a non-explicit constructor still compiled under the same non-explicit version.
    I know explicit prevents implicit type convertions, but can't seem to make that have an effect when it comes to a converting constructor with an initializer_list.
    My text insists there's a signifigant difference, and that it's a design consideration, but I can't get any demonstrable effect, practical or otherwise.
    Eventually, I decided that thinking was not getting me very far and it was time to try building.
    — Rob Pike, "The Text Editor sam"

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    It basically means a temporary Foo can't be implicitly created from an initialiser list.

    For example;
    Code:
    class Foo
    {
        public:
    
           explicit Foo(std::initializer_list<std::string> il);
    };
    
    void f(Foo);
    
    int main()
    {
         f({"A", "B"});     // this will not compile if the constructor is explicit.
    }
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    My text insists there's a signifigant difference, and that it's a design consideration, but I can't get any demonstrable effect, practical or otherwise.
    Your text, though you didn't say what it said, is almost certainly correct.

    There is a significant difference regardless of the details of the text.

    However, `std::initializer_list' isn't all that important; the difference of between implicit and explicit constructors apply almost regardless of the types involved in the constructors parameters as long as the given constructor can be called with only one real parameter.

    I've included what is likely to be the most common case which readily yields arguments for and against explicit constructors.

    Ultimately, the choice is a simple one: do you want to allow automated construction of a class from an arbitrarily implied value?

    Unfortunately, the choice is has far reaching implications. The example actually only suggests one immediate problem. When more sophisticated polymorphisms are at play the problems may be more subtle such as entirely changing the meaning of a program.

    However, there is a fairly simple "rule-of-thumb": unless the class in question represents an object intended to closely mimic native types, such as a "big number" class, prefer to use explicit conversions.

    Soma

    Code:
    #include <initializer_list>
    #include <iostream>
    
    struct Tester1
    {
        Tester1(std::initializer_list<int>)
        {
        }
    };
    
    struct Tester2
    {
        Tester2(std::initializer_list<int>)
        {
        }
    };
    
    struct Tester3
    {
        explicit Tester3(std::initializer_list<int>)
        {
        }
    };
    
    void Test1(const Tester1 &)
    {
    }
    
    void Test2(const Tester1 &)
    {
    }
    
    void Test2(const Tester2 &)
    {
    }
    
    void Test3(const Tester3 &)
    {
    }
    
    int main()
    {
        Test1({1,2,3}); // fine
        //Test2({1,2,3}); // ambiguous
        Test2(Tester1({1,2,3})); // fine
        Test2(Tester2({1,2,3})); // fine
        //Test3({1,2,3}); // error no implicit conversion path
        Test3(Tester3({1,2,3})); // fine
        return(0);
    }

  4. #4
    C++ Enthusiast M.Richard Tober's Avatar
    Join Date
    May 2011
    Location
    Georgia
    Posts
    56

    Thanks!

    Thanks guys. Grumpy, your code still compiled for me, and is very close to my original test-case, but I think I got it now. I was confusing creating an instance of the object with actually using it in a function call. The text asked me to figure out the "pros and cons" of making the constructor intentionally non-explicit.

    I see now making it explicit would require the user to explicity use a StrBlob, where as a non-explicit constructor allows for a much more natural construction from a braced-list of std::strings, or c-style strings.

    I admit, I really can't see any downsides to a non-explicit constructor in this case. What are the chances someone unintentionally runs a function with a brace-enclosed list (which always converts to a std::initializer_list) and doesn't mean to run StrBlob... or is this specifically to avoid ambiguity if there's two or more classes who all have converting-constructors which take std::initializer_list's... I think I get it... again.

    Then in large projects, non-explicit converting-constructors (single-argument constructors) must be very dangerous indeed...

    Anyhow, here's my code. I won't take the explicit keyword for granted in the future!
    Code:
    #include <iostream>
    #include <string>
    #include <initializer_list>
    
    class StrBlob {
      public:
        explicit StrBlob(std::initializer_list<std::string> il) {};
        StrBlob(std::initializer_list<std::string> il) {};
    };
    
    void Blobber(const StrBlob&) {} 
    
    int main() {
        StrBlob sb({"Fred", "Wilma", "Barney"}); // This is not a conversion
        
        // Fails if constructor is explicit, requires implicit conversion
        Blobber({"Larry", "Curly", "Moe"}); 
        
        // Works explicit or implicit constructor:
        Blobber(StrBlob({"Larry", "Curly", "Moe"}));
        Blobber(StrBlob(std::initializer_list<std::string>{"Larry", "Curly", "Moe"}));
        Blobber(sb);
            
        return 0;
    }
    Eventually, I decided that thinking was not getting me very far and it was time to try building.
    — Rob Pike, "The Text Editor sam"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Help with implicit and explicit constructor calls
    By mborba22 in forum C++ Programming
    Replies: 1
    Last Post: 09-22-2010, 03:49 PM
  2. Interesting behavior with explicit copy constructor
    By Clairvoyant1332 in forum C++ Programming
    Replies: 1
    Last Post: 04-28-2008, 03:19 PM
  3. A Question about explicit constructor
    By meili100 in forum C++ Programming
    Replies: 1
    Last Post: 07-08-2007, 04:08 PM
  4. Explicit constructor on strings
    By Mario F. in forum C++ Programming
    Replies: 5
    Last Post: 06-21-2006, 02:46 PM
  5. Templated Non-Explicit Constructor
    By golfinguy4 in forum C++ Programming
    Replies: 2
    Last Post: 08-05-2003, 02:37 PM

Tags for this Thread