ikh
ikh

Reputation: 10427

Why can I use private default constructor in decltype()?

Look at the code:

#include <iostream>
#include <utility>

class test
{
private:
    test() { }
public:
    test foo() { return *this; }

    static const char *name() { return "test"; }
};

int main()
{
    std::cout << decltype(test().foo())::name() << std::endl;               // 1
    std::cout << decltype(std::declval<test>().foo())::name() << std::endl; // 2
}

I expected // 1 line cannot be compiled because the default constructor of test is private.

However, it works well. I tested it on my g++ 4.8.3 with -Wall -Wextra -Werror -pedantic in disbelief, but it works well without any errors or warnings.

(In addition, it seems to work well in GCC 4.9.1 as well.)

From this page, I guess we can use private default constructor if the expression is unevaluated. So, I tested the following to check it.

#include <iostream>
#include <utility>

class test
{
private:
    test(int) { }
public:
    test foo() { return *this; }

    static const char *name() { return "test"; }
};

int main()
{
    std::cout << decltype(test().foo())::name() << std::endl;               // 1
    std::cout << decltype(std::declval<test>().foo())::name() << std::endl; // 2
}

(live example)

As expected, it wasn't compiled.

But.... why?? How can it be possible? Can we use private members in unevaluated expression? Or is there a special rule for default constructors? Could you explain me why?

Upvotes: 9

Views: 1501

Answers (1)

Mike Seymour
Mike Seymour

Reputation: 254681

It shouldn't compile. C++11 [class.temporary] has this to say about creating a temporary object:

12.2/1 Even when the creation of the temporary object is unevaluated or otherwise avoided, all the semantic restrictions shall be respected as if the temporary object had been created and later destroyed. [ Note: even if there is no call to the destructor or copy/move constructor, all the semantic restrictions, such as accessibility and whether the function is deleted, shall be satisfied. However, in the special case of a function call used as the operand of a decltype-specifier, no temporary is introduced, so the foregoing does not apply to the prvalue of any such function call. — end note ]

So, even when unevaluated, you're still restricted by the accessibility of any functions (including constructors) needed to create and destroy the temporary. The final sentence of the note clarifies that a function like declval can be used to avoid this obstacle.

Upvotes: 13

Related Questions