1

My question is, whether the following code is valid:

template<int i> class Class
{
    static_assert(sizeof(i) == 0, "Class instantiated with i != 1");
};

template<> class Class<1> {};

This snippet compiles with g++. But clang++ is trapped by the static_assert:

error: static_assert failed "Class instantiated with non-int type"

A template that uses a type instead of an int like

template<typename T> class Class
{
    static_assert(sizeof(T) == 0, "Class instantiated with non-int type");
};

template<> class Class<int> {};

is accepted by both compilers. Exactly the same pattern applies to function templates.

I found open-std.org::Non-dependent static_assert-declarations, but that does not seem to apply, because my static_assert is dependent on the template parameter.

You may check the described behavior on godbolt.org

EDIT: As Johan Lundberg points out in the comment my question is wrong. Indeed sizeof(i) does not depend on the template parameter. Also R.Sahu is completely right: It would make much more sense to assert i != 1. For this again both compilers accept the code.

However, still the upper example compiles with g++. As open-std.org::Non-dependent static_assert-declarations applies to that case (I apologize again for the wrong question in this respect): Is g++ actually wrong in compiling the code without error?

9
  • 5
    I don't think you are correct in "because my static_assert is dependent on the template parameter.". sizeof(i) does not depend on the value of i. Commented Apr 7, 2017 at 21:15
  • Why do you use sizeof in the first example? i is of type int and sizeof(i) is the same as sizeof(int). Perhaps in the assertion you want to use i directly. Commented Apr 7, 2017 at 21:16
  • 2
    Did you mean to use static_assert(i != 1, "Class instantiated with i != 1"); by any chance? Commented Apr 7, 2017 at 21:23
  • 1
    Not defining the primary template is the way the Standard goes, for example std::function<T>. You can also define a template<int N> struct always_false : public std::false_type {}; and do static_assert(always_false<i>::value, "My error"); Commented Apr 8, 2017 at 13:15
  • 1
    This discussion led me to file gcc bug 80368 gcc.gnu.org/bugzilla/show_bug.cgi?id=80368 Commented Apr 8, 2017 at 13:16

2 Answers 2

1

clang++ is right to reject your code, but g++ is not wrong to fail to catch the error; this is a "no diagnostic required" situation.

The Standard strictly defines expressions within a template as "type-dependent" and/or "value-dependent". Given template<int i>, i is value-dependent but not type-dependent.

[14.6.2.2/4]: Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):

  • ...
  • sizeof unary-expression
  • ...

[14.6.2.3/2]: Expressions of the following form are value-dependent if the unary-expression or expression is type-dependent or the type-id is dependent:

  • sizeof unary-expression
  • ...

So sizeof(i) is not dependent.

Finally, 14.6/8 says:

If a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, the program is ill-formed; no diagnostic is required.

Sign up to request clarification or add additional context in comments.

Comments

0

Firstly, if you want to assert that the template has been instanciated with the wrong type, you must be explicit in the static_assert:

#include <type_traits>

template<typename T> class Class
{
    static_assert(std::is_same<T, int>::value, "Class instantiated with non-int type");


};

Secondly, the following line may not be doing what you think:

template<> class Class<int> {};

What you're actually doing here is creating a whole new specialisation, which has nothing whatsoever to do with the default template specialisationClass<T>.

Deleting the specialisation for int and instanciating a non-int class of the default specialisation yields an error in both compilers as it should:

int main()
{
    Class<int> ci;
    Class<float> cf;
}

example error:

<source>: In instantiation of 'class Class<float>':
<source>:14:18:   required from here
<source>:5:5: error: static assertion failed: Class instantiated with non-int type
     static_assert(std::is_same<T, int>::value, "Class instantiated with non-int type");
     ^~~~~~~~~~~~~

3 Comments

Thanks for your answer. But I think your idea is somewhat different to mine. I think of a class that has no (useful) default implementation, but offers several (not only one) specializations. By this I can select the appropriate implementation during compilation.The static_assert provides only a readable error message.
@marlam right... but remember that each specialisation that you define represents a whole new class, which does not have the static_assert in it. You are defining Class<int>, not instantiating it.
Correct, that is what I meant. It would counteract the idea if the static_assert were in every specialization. However, we are now still with the question, why g++ accepts the first snippet (see the edit in the question).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.