Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template-based "override" equivalent when partial-specializing?

I have a template class/struct that looks like this:

template <typename T, typename U>
struct S
{
    unsigned int operator()(T t, U u) const;
};

And I would like to make sure that specializations respect this interface.

Unfortunately, I can specialize this struct with a different return type. For instance if I partial-specialize to return bool instead of unsigned int, I would expect to get a compiler error, but the compiler does not seem to care:

template <typename T>
struct S<T,nullptr_t>
{
    bool operator()(T t, nullptr_t u) const { return 2; }
};

Example @ Ideone.com

In the above example, the specialized version is supposed to return 2 but since the return type is bool, the return value is converted to true which is then displayed as 1.

Why does the compiler accept this?

How can I prevent programmers from specializing the template with a wrong return type (or even with wrong arguments)?

I know I can achieve what I want with a virtual method in a base template class/struct and use the override keyword in children:

Solution with override (does not compile, which is good)

But having a virtual method will certainly create a virtual table and I would like to avoid that as much as possible, especially since I do not need the virtual table at run-time. Unless there is a trick to do the same without building a virtual table?

Also, I know the problem would be simpler if I could partial-specialize methods or if I could rely on non-partial-specialization, but the former is not possible in C++ AFAIK and the latter does not cover the case I need in my program.

like image 373
vdavid Avatar asked Aug 09 '18 09:08

vdavid


People also ask

What is template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

When we specialize a function template it is called?

To do so, we can use a function template specialization (sometimes called a full or explicit function template specialization) to create a specialized version of the print() function for type double.

Can a template function override?

You cannot define a virtual template method. override only works for virtual methods, and you can only override methods with the same signature.

What does template <> mean in C++?

What Does Template Mean? A template is a C++ programming feature that permits function and class operations with generic types, which allows functionality with different data types without rewriting entire code blocks for each type.


1 Answers

A good way to create static interface is usage of curiously recurring template pattern. In Your situation it would look like this:

template<class Derived, class T, class U>
constexpr bool MyTrait = std::is_same<unsigned int, decltype(std::declval<Derived>()(std::declval<T>(), std::declval<U>()))>::value;

template <typename Derived, class T, class U>
struct StaticInterface
{

    unsigned int operator()(T t, U u) const{
        static_assert( MyTrait<Derived, T, U>, "errr" );

        return (*static_cast<const Derived *>(this))(std::forward<T>(t), std::forward<U>(u));
    }
};

template <typename T, typename U>
struct S : StaticInterface<S<T, U>, T, U>
{
    unsigned int operator()(T t, U u) const{ /* some implementation */}
};

template <typename T>
struct S<T, std::nullptr_t> : StaticInterface<S<T, std::nullptr_t>, T, std::nullptr_t>
{
    bool operator()(T t, std::nullptr_t u) const { return 2; }
};

To get it working the function call must be done via interface like this:

template<class Derived, class T, class U>
void test(const StaticInterface<Derived, T, U> &inter){
    inter(T(), U());
}

Otherwise the operator from derived will be picked as the preferable one.

like image 74
bartop Avatar answered Sep 21 '22 22:09

bartop