Template with auto-type return deduction

Viewed 198

I have created a templated struct and am trying to overload the binary operators. For some reason the function does not return the correct data type even though the type is correctly casted in between.

template<typename T>
struct Number{
    const T value;
    Number(T a) : value(a) {}

    template<typename U>
    auto operator*(Number<U> other){
        auto new_value = value*other.value;
        std::cout << typeid(new_value).name() << std::endl;
        return Number(new_value);
    }
};

Now if I perform this operation with the following code called in main. It returns Number of the type of the first one, not number of the higher type.

auto b =  Number<int>(6) * Number<double>(2.3); // this should be an int*double=double
std::cout << b.value << typeid(b.value).name() << std::endl;
auto c = Number<double>(2.3) * Number<int>(6);
std::cout << c.value << typeid(c.value).name() << std::endl;

Output is as follows: d 13i d 13.8d

From what I understand, the incorrect constructor is called when the function return the new Number(new_value). I do not understand how and why this happens as new_value is of the 'correct type'.

3 Answers

Inside the scope of a template, the template-name is going to stand for the injected class name, and not for the template. So there will not be CTAD, and that's by design

Using return Number<decltype(new_value)>(new_value); is the simple workaround.

You are return the first type and not the second type:

template<typename U>
auto operator*(Number<U> other){
    auto new_value = value*other.value;
    std::cout << typeid(new_value).name() << std::endl;
    return Number(new_value);
}

Even if new_value is a double, you are storing it in a Number<T>.

Try:

template<typename U>
auto operator*(Number<U> other){
    auto new_value = value*other.value;
    std::cout << typeid(new_value).name() << std::endl;
    return Number<decltype(new_value)>(new_value);
}

As StoryTeller points out, within a class template definition, the name of the class refers to the specific class instance (known as the injected-class-name) and not the name of the template.

But if you want class template argument deduction to still apply, you can just qualify the name:

return ::Number(new_value);

::Number refers to the class template Number, not the specific type Number<T>. But this might be a little too magic for other people reading your code, and simply using Number<decltype(new_value)>(new_value) has a lot of benefit.

Related