2

It is possible to check for the existence of class member functions. An implementation of the check could be taken from this answer: https://stackoverflow.com/a/257382/2492801.

Now a static_assert can be used to ensure that a certain class has an implementation of a needed method. But if the class is templated, I do not know whether such a static assertion is possible or how to do it - except if I do the static_assert for a concrete template parameter selection. The latter is possible, but it feels wrong...

Please consider the following code:

#include <iostream>

// SFINAE test
template <typename T>
class has_helloworld
{
    typedef char one;
    struct two
    {
        char x[2];
    };

    template <typename C>
    static one test(decltype(&C::helloworld));
    template <typename C>
    static two test(...);

  public:
    enum
    {
        value = sizeof(test<T>(0)) == sizeof(char)
    };
};

template <int number>
struct Hello
{
    int helloworld()
    {
        return 0;
    }

    // The next line is not possible since class Hello is not complete yet.
    // static_assert(has_helloworld<Hello<number>>::value);
};

template <int number>
struct Generic
{
};

int main()
{
    // This is possible, but I don't like it
    static_assert(has_helloworld<Hello<3>>::value);

    // The next two lines are not possible because a template declaration cannot appear at block scope.
    // template <int number>
    // static_assert(has_helloworld<Hello<number>>::value);

    std::cout << has_helloworld<Hello<2>>::value << std::endl;
    std::cout << has_helloworld<Generic<2>>::value << std::endl;

    return 0;
}

Here is a Godbolt link: https://godbolt.org/z/c3bKKMxcc

Is there a possibility in C++ to do a "templated static assert", so a static_assert where I check a property of a class that depends on a template parameter without choosing a dummy value for that parameter?

Clarification: In my case in practice the template parameter does not play a role, it just impedes the static_assert. So like in the code example, all template structs Hello have the required method regardless of the parameter number.

Benjamin Bihler
  • 1,612
  • 11
  • 32
  • 2
    Perhaps important to your question - is there a template specialization of `Hello` anywhere that does not implement `helloworld()`? Are you expecting all specializations to be considered in your assert? – Drew Dormann Jul 17 '23 at 15:59
  • Since there can be specialization of templates then what should happen if one specialization of `Hello` has `helloworld` and other is not? There is no way to list all possibilities of template. – Marek R Jul 17 '23 at 16:20

3 Answers3

4

You can achieve something with a poisoned type:

template <int N>
struct assert_hello_has_helloworld_impl
{
    static_assert(has_helloworld<Hello<N>>::value);
};

Add sugar to the cake:

template <int N>
void assert_hello_has_helloworld(assert_hello_has_helloworld_impl<N> = {}) {}
// ...
assert_hello_has_helloworld<3>();

Full demo: https://godbolt.org/z/7z9nzPqWs

YSC
  • 38,212
  • 9
  • 96
  • 149
2

It is unclear what you are exactly expect. I understand your question that: you wish to check if template has some function without explicitly specifying template parameter in this check.

Problem is that every template can have template specialization, and check can be performed only of specific type. As a workaround you can hide this parameter inside of code which verifies function existence:

template <template <std::size_t> typename T, typename = void>
struct has_helloworld : std::false_type { };

template <template <std::size_t> typename T>
struct has_helloworld<T, decltype(std::declval<T<0>>().helloworld())> : std::true_type { };

template <template <std::size_t> typename T>
constexpr bool has_helloworld_v = has_helloworld<T>::value;

So now static assertion do not explicitly states parameters:

static_assert(!has_helloworld_v<NoHelloworld>);
static_assert(has_helloworld_v<WithFuncHelloworld>);

Here is a demo: https://godbolt.org/z/E8r7eTqde

Problem is that this only check installation of template for parameter 0 (or other predefined list of parameters). This makes this code fragile.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • 1
    Another way to understand the OP's error: Classes are not "templated". Template classes are not classes. Template classes generate classes when passed parameters. – Yakk - Adam Nevraumont Jul 17 '23 at 19:04
  • @Yakk-AdamNevraumont Your comment and this answer have helped me to understand my mistake. My mistake has been the assumption that every class `Hello` has the method `helloworld()` (because it is declared in the source code). But with class template specialization it is very well possible to have a class `Hello` with a certain `n` that doesn't have this method. Therefore the `static_assert` can only happen within the scope of `Hello` or with concrete template parameter `n`, if it should happen in another scope. – Benjamin Bihler Jul 18 '23 at 08:22
2

Template classes are not classes.

Template classes generate classes.

Asking if a template class has a specific member is a category error. It is like asking if a specific type of paper has mountains drawn on it.

You might say "but I know that this paper is always used to draw maps of the rockies!" C++ doesn't know this, it just knows you are talking about paper.

The mapping of template class plus parameters to class is a Turing complete computation. This process cannot be inverted in the general case - for a template class X<A>, you cannot answer the question "what properties does X<> have for an arbitrary A".

When designing the template system for C++, the computational model chosen was too strong to be inverted like that.

It is true that the most typical use of templates uses a far weaker set of computational capabilities - most uses of templates are a step removed from macros with basic meta type checking.

As a concrete example, a template class can be written such that Hello<N> has a specific property if and only if the Collatz conjecture is true for the constant N, and it accepts bignums as arguments.

In order to know the properties of Hello<?> for an arbitrary argument, the compiler would have to prove the Collatz conjecture.

Now, you might say, "I'm not doing that". Too bad; C++ doesn't say "so long as the person writing the template doesn't do anything fancy, you can invert the template mapping and solves this problem".

It could; it could define a weaker set of template mechanics that can be safely inverted without hitting this problem. A number of languages do exactly that (Generics in Java, which both weaken the kind of mapping you can do and have a rather different implementation; C#'s version is a bit stronger than Java, but also weakens C++ templates in a somewhat similar way).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524