Notification texts go here Contact Us Buy Now!

static_assert in templated member function of non-template class

```html

In C++, static_assert is a compile-time assertion that verifies whether a condition is true at compile time. If the condition is false, the compiler will issue an error message and stop the compilation process.

When using static_assert in a templated member function of a non-template class, you may encounter an error if the assertion condition is false and the function is never instantiated. This is because some compilers have not implemented a solution for DR2518, which states that:

  • If the value of the expression when so converted is true or the expression is evaluated in the context of a template definition, the declaration has no effect. Otherwise, the static_assert-declaration fails

Since you have static_assert(false, "..."); in a template definition that is uninstantiated, it should not be refused by the compiler.

Implementations which haven't implemented a solution for the above defect report still abide by the old rules, shown in the old part of this answer:


[dcl.pre]/10

In a static_assert-declaration, the constant-expression is contextually converted to bool and the converted expression shall be a constant expression ([expr.const]). If the value of the expression when so converted is true, the declaration has no effect. Otherwise, the program is ill-formed, and the resulting diagnostic message ([intro.compliance]) should include the text of the string-literal, if one is supplied.

By putting false in your static_assert you've made the program ill-formed and the compiler is correct when rejecting the program.


A possible workaround could be to make the assertion dependent on the template parameter, at least superficially.

template<class>
struct always_false : std::false_type {};

template<class T>
inline constexpr bool always_false_v = always_false<T>::value;

class TestClass {
    size_t MemberFn() { /* shared stuff between trivial and non-trivial */
        return 0;
    }
    template <typename Type>
    size_t MemberFn(
        std::enable_if_t<!std::is_trivially_constructible_v<Type> ||
                         !std::is_trivially_destructible_v<Type>>* = nullptr) {
        static_assert(always_false_v<Type>, "not implemented yet");
        return 0;
    }
    template <typename Type>
    size_t MemberFn(
        std::enable_if_t<std::is_trivially_constructible_v<Type> &&
                         std::is_trivially_destructible_v<Type>>* = nullptr) {
        static_assert(always_false_v<Type>, "not implemented yet");
        return 0;
    }
};

Another answer:

Raymond Chen covered this issue in his blog:

How can I create a type-dependent expression that is always false?

auto lambda = [total](auto op, auto value) mutable
{
  ...
  static_assert(false, "Don't know what you are asking me to do.");
  ...
}; 

However, this does not compile because the static_assert fails immediately.

The reason is that the controlling expression for the static_assert is not dependent upon the type of the arguments, and therefore it is evaluated when the lambda is compiled, not when the lambda is invoked (and the implicit template instantiated).

In order to defer the static_assert to instantiation, we need to make it type-dependent.

Of what use is a type-dependent expression that is always false?

Last time, we saw how to create a type-dependent expression that is always false, and used it in a potentially-discarded statement so that the assertion failed only if the statement ended up being used.

Another case where you want to defer a static assertion failure to instantiation is if you want to reject a particular specialization.

So, you need to make the static_assert condition be dependent on the template argument. Raymond showed two ways to do that:

Using a custom helper template:

#include <type_traits>

template<typename>
inline constexpr bool always_false_v = false;

class TestClass
{
    ...

    template<typename Type>
    size_t MemberFn(std::enable_if_t<!std::is_trivially_constructible_v<Type> || !std::is_trivially_destructible_v<Type>>* = nullptr)
    {
        static_assert(always_false_v<Type>, "not implemented yet");
        return 0;
    }

    template<typename Type>
    size_t MemberFn(std::enable_if_t<std::is_trivially_constructible_v<Type> && std::is_trivially_destructible_v<Type>>* = nullptr)
    {
        static_assert(always_false_v<Type>, "not implemented yet");
        return 0;
    }
};

Using sizeof():

#include <type_traits>

class TestClass
{
    ...

    template<typename Type>
    size_t MemberFn(std::enable_if_t<!std::is_trivially_constructible_v<Type> || !std::is_trivially_destructible_v<Type>>* = nullptr)
    {
        static_assert(!sizeof(Type*), "not implemented yet");
        return 0;
    }

    template<typename Type>
    size_t MemberFn(std::enable_if_t<std::is_trivially_constructible_v<Type> && std::is_trivially_destructible_v<Type>>* = nullptr)
    {
        static_assert(!sizeof(Type*), "not implemented yet");
        return 0;
    }
};
```

Post a Comment

Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.