Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to match a nested template type implicitly in C++?

Tags:

c++

templates

I have the following MWE:

#include <iostream>
#include <memory>

class A {
public:
    int n = 42;
    typedef std::shared_ptr<A> Ptr;
};

template<typename T>
void foo(typename T::Ptr arg) {
    std::cout << arg->n << std::endl;
}

template<typename T>
void bar(T arg) {
    std::cout << arg.n << std::endl;
}

int main() {
    A::Ptr a = A::Ptr(new A());
    foo<A>(a); // Can I avoid giving <A> here explicitly.
    // foo(a); // does not compile
    bar(*a); // after all this does work
    return 0;
}

To me it looks like it should also be possible to call foo(a) instead of foo<A>(a). Why is this not possible and can I somehow change the definition of foo to make this possible?

I realize that I could just skip the ::Ptr in the signature, but I still want to have access to the A type without the pointer.

like image 228
Dimitri Schachmann Avatar asked Aug 26 '16 09:08

Dimitri Schachmann


2 Answers

That is not possible because that is non-deducible context.

The type of a is just std::shared_ptr<A>, which means if foo(a) works, then the following should also work:

std::shared_ptr<A> x(new A());
foo(x);

If so, then what should T be deduced to — and why? You might be tempted to say "T should be deduced to A, because A has a nested type Ptr which is same as std::shared_ptr<A>". Well, what if there is another class defined as:

struct B
{
    typedef std::shared_ptr<A> Ptr;
};

What should T be deduced to? A or B? or something else?

Here is another topic that discusses non-deducible context using a different example:

  • C++, template argument can not be deduced

Hope that helps.

like image 124
Nawaz Avatar answered Oct 16 '22 23:10

Nawaz


Nawaz's answer has explained why the code doesn't work, I'll focus on this question:

but I still want to have access to the A type without the pointer.

std::shared_ptr has a member type element_type, you could use it like typename T::element_type. And if you want the code works well with raw pointer too, you could provide a trait class template:

template <typename T>
struct trait_element_type {
    using element_type = std::remove_pointer_t<T>;
};

template <typename T>
struct trait_element_type<std::shared_ptr<T>> {
    using element_type = T;
};

And then use it as:

template<typename T>
void foo(T arg) {

    std::cout << arg->n << std::endl;

    typename trait_element_type<T>::element_type x; // retrieve the element type
    std::cout << x.n << std::endl;
}

LIVE

like image 25
songyuanyao Avatar answered Oct 17 '22 01:10

songyuanyao