Home > Mobile >  Avoiding template parameter substitution completely
Avoiding template parameter substitution completely

Time:12-01

I have a class that can accept arithmetic types and std::complex. A simplified code of the class is

#include <complex>

template<typename T> struct is_complex : std::false_type {};
template<typename T> struct is_complex<std::complex<T>> : std::true_type {};

template<class T>
struct Foo {
    void foo(typename T::value_type t)
    requires (is_complex<T>::value) {
    }
};

Now, I would like to take the internal type of std::complex and use it as the type of the parameters in the foo function.For example, if T is std::complex<double>, then I want the parameter types to be double.

This function should only be available when T is indeed std::complex.

I thought I could use typename T::value_type as the parameter type, since std::complex has a typedef value_type. Plus, I thought using requires here would avoid T to be substitued in this function in case T wasn't std::complex. Silly me. The issue is that whenever I create a Foo<FundamentalType> the code breaks, since fundamentals don't have ::value_type.

int main() {
    Foo<int> obj; // Breaks the code.
    //obj.foo(4); // Function shouldn't be considered in overload resolution ideally...

    Foo<std::complex<int>> obj2; // Works
    obj2.foo(4); // Works as expected
}

Ideally, I would like the substitution of T to be ignored for this function in case T is not std::complex. Is that possible? If not, how can I circumvent this?

CodePudding user response:

You're on the right track with is_complex: you'd like the same here, but with a different body of the type. For example,

template<typename T> struct complex_value_type {};
template<typename T> struct complex_value_type<std::complex<T>> { using type = T; };

template<typename T>
using complex_value_type_t = typename complex_value_type_t<T>;

Then, at any point, you can call it as complex_value_type_t<T>:

template<class T>
struct Foo {
    void foo(complex_value_type_t<T> t)
    requires (is_complex<T>::value) {
    }
};

The requires is not absolutely necessary then; it's already covered by complex_value_type_t<T> being defined only for complex<T>.

CodePudding user response:

You just need some type to put in there, until requires can disable the function.

I would do this:

struct nullptr_value_type {using value_type = std::nullptr_t;};
using elem_or_null_t = typename std::conditional_t<is_complex<T>::value, T, nullptr_value_type>::value_type;
    
void foo(elem_or_null_t t)
requires (is_complex<T>::value)
{}

CodePudding user response:

Use a template class as a template parameter.

#include <complex>

template<template<class> class T> struct is_complex : std::false_type {};
template<> struct is_complex<std::complex> : std::true_type {};

template<template<class> class T>
struct Foo {
    void foo(typename T<double>::value_type t)//could be typename<T<TT>> if you made foo a templated function
    requires (is_complex<T>::value) {
    }
};
int main(){
    Foo<std::complex> f;
};

But you will need what to put into it when used. I just hard-coded double but you have to add a new template parameter to Foo or make foo a templated member function

  • Related