Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::is_constructible returns inconsistent value for private constructor

Tags:

c++

typetraits

What are the rules by which std::is_constructible handles private constructors? Given the following code:

#include <iostream>

class Class {
private:
    Class() { }
};

template <typename T>
class Test {
public:
    static void test() {
        std::cout
            //<< std::is_constructible<Class>::value
            << std::is_constructible<T>::value
            << std::endl;
    }
};

int main() {
    Test<Class>::test();
}

This prints 0 (ideone), i.e., T is not default constructible.

Uncommenting the commented line, it prints 11 (ideone), so T suddently became default constructible.

I could find reasoning to support both results, but I don't understand how including the commented line changes the result of the second. Is this somehow invoking UB? Is this a compiler bug? Or is std::is_constructible really that inconsistent?

like image 976
zennehoy Avatar asked Mar 02 '20 14:03

zennehoy


1 Answers

std::is_constructible should return false in this scenario because the constructor is not accessible.

As pointed out underneath the question, the behavior described in the question is caused by a bug in GCC / libstdc++. The bug is reported here, and, according to the Bugzilla, related to if not caused by an access control bug for classes in template functions that has been unresolved for quite a while. The relationship between the two bugs is taken from Jonathan Wakely's Bugzilla comment who seems to have detected the connection between the two bugs first.

This is also implied by the fact that the behavior of this scenario in GCC becomes correct when deleting the constructor instead of making it private:

class Class {
    Class() = delete;
};

which prints out 0 and 00 respectively. This is the correct output (which clang correctly reports in the scenario with a private constructor aswell).

This could explain the observed change of behavior when commenting in the line, because inside the function in the templated struct, access checking doesn't work and reports that the constructor is accessible when it's not. When the trait is checked again in the next line or possibly at a completely different location (as is the case here), it has already been instantiated, and thus yields the wrong answer.

like image 153
mutableVoid Avatar answered Oct 30 '22 21:10

mutableVoid