Short-circuiting while instantiating template?
Asked Answered
F

2

12

Consider this code snippet,

template<bool b>
struct other
{
    static const bool value = !b;
};

template<bool b>
struct test
{
    static const bool value = b || other<b>::value;
};

int main()
{
      bool value = test<true>::value;   
}

Do compilers instantiate other<true> in situations such as the above, when instantiating seems completely unnecessary? Or just because I've written the syntax other<b>::value, compilers must instantiate it regardless of the fact that it contributes absolutely nothing to the calculation of the value of test<true>::value?

I would like to hear, a) what is required by the Standard, and b) what is actually implemented by the various compilers? Relevant sections from the Standard would be appreciated.

Fennie answered 5/1, 2011 at 3:24 Comment(2)
According to "C++ Templates: The Complete Guide," page 307, it looks like, at least according to the standard, that no, the compiler will not short-circuit this expression and will indeed evaluate both branches. I'm not sure exactly why this is or what the more general principle in action here is, but this might be at least a partial answer to your question.Nogood
The Standard doesn't allow them to do "short circuit" here. That's why you need to implement it yourself, as done here. The reason is simple: You need to know the type of other<b>::value and whether it is convertible to bool at all and makes sense. For doing so other<b> needs to be instantiated. What if its type would yield to an overloaded op|| being called? The initializer would be invalid then.Mercury
N
9

According to the C++ spec, section $14.7.1/4:

"A class template specialization is implicitly instantiated if the class type is used in a context that requires a completely-defined object type or if the completeness of the class type affects the semantics of the program; in particular, if an expression whose type is a class template specialization is involved in overload resolution"

In the case you illustrated with short-circuiting, the class would have to have a complete type, because you're looking inside of it to find the value static member. This precludes the compiler from short-circuiting the expression.

As for what actually happens in practice, I'm not sure because I can't see how the compiler could get away with not doing the instantiation. For example, suppose that the instantiation of other<b> looked like this:

template <bool B> struct other {
    typedef int value;
};

Here, your program would be ill-formed because other<b>::value is a type, not a value, but the compiler couldn't diagnose the error without actually doing the instantiation.

Nogood answered 5/1, 2011 at 4:7 Comment(0)
G
0

Note that since the introduction of constexpr functions in C++11, you can achieve short-circuiting:

template<bool b>
constexpr bool test_other();

template<bool b>
struct test
{
    static const bool value = b || test_other<b>();
};

int main()
{
    bool value = test<true>::value;
}

... though test_other() is undefined the compiler doesn't attempt to call it.

Unfortunately this doesn't work with consteval. In this case the first-level 'test facility' test itself must be a function:

template<bool b>
consteval bool test_other();

template<bool b>
consteval bool test()
{
    return b || test_other<b>();
};

int main()
{
    bool value = test<true>();
}
Gus answered 25/11, 2021 at 22:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.