Björn Pollex
Björn Pollex

Reputation: 76788

Workaround for template argument deduction in non-deduced context

Consider the following code:

#include <iostream>

template<class T>
struct outer {
    struct inner {};
};

template<class T>
std::ostream& operator<<(std::ostream & stream, 
                         typename outer<T>::inner const& value) {
    std::cout << "An outer::inner!";
    return stream;
}

int main() {
    outer<float>::inner foo;

    std::cout << foo << std::endl; // does not compile
}

This does not compile, because typename outer<T>::inner is a nondeduced context (as explained here), meaning the template-argument-type cannot be deduced by the compiler (read this answer for the why). As I see it, I have two options to make it work:

  1. Move inner outside of outer and make it a class-template. I prefer this one, because the impact on the using code is smaller.
  2. Add a to_string-method to inner.

Are there any other solutions for this (that do not result in ugly syntax in the using code)?

Upvotes: 17

Views: 1550

Answers (2)

aint-no-programmer
aint-no-programmer

Reputation: 1

As I suppose, it is the nested-name-specifier case of the non deduced context, see cppreference. But it does not mean that you can't specify template types for operator<< explicitly.
It works:

#include <iostream>

template<class T>
struct outer {
    struct inner {};
};

template<class T>
std::ostream& operator<<(std::ostream& stream,
    typename outer<T>::inner const& value) {
    std::cout << "An outer::inner!";
    return stream;
}

int main() {
    outer<float>::inner foo;

    //std::cout << foo << std::endl; // does not compile
    operator<<<float>(std::cout, foo);  
}

Upvotes: 0

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 506985

You can move the operator into the inner class body and put friend before it. Then replace the parameter type by just inner.

Another technique is to derive inner from a CRTP base parameterized by inner. Then make the parameter type the CRTP class and cast the parameter reference to the derived inner class, the type of which is given by the template argument you deduce.

Upvotes: 23

Related Questions