Search code examples
c++c++17c++14c++20

Tagged structures casting rules


I was playing around with toy tuple implementations and eventually stuck with how get function works.

Consider this simple example

#include <iostream>
#include <utility>

template <size_t Tag, typename ValueType>
struct TagedValue { ValueType value; };

struct Test : TagedValue<0, int>, TagedValue<1, std::string>, TagedValue<2, double> {};

template <size_t Idx, typename T>
auto& get(Test& test) {
    ((TagedValue<Idx, T>&)(test)).value;
}

template <size_t Idx, typename T>
auto& get_impl(TagedValue<Idx, T>& tagged_value) {
    return tagged_value.value;
}

template <size_t Idx>
auto& get_2(Test& test) {
    return get_impl<Idx>(test);
}


int main()
{
    Test test;
    get_2<0>(test);
    get<0>(test);
}

I get this error:

<source>: In function 'int main()':
<source>:29:16: error: no matching function for call to 'get<0>(Test&)'
   29 |     get<0>(test);
      |                ^
<source>:10:7: note: candidate: 'template<long unsigned int Idx, class T> auto& get(Test&)'
   10 | auto& get(Test& test) {
      |       ^~~
<source>:10:7: note:   template argument deduction/substitution failed:
<source>:29:16: note:   couldn't deduce template parameter 'T'
   29 |     get<0>(test);
      |                ^

I do have couple of questions:

  1. Basically why get_2 works and get doesn't compile. To me it looks like get_2 does exactly what I'm trying to do inside get
  2. Does deducing T for get_2 take O(1) time, if yes how is it possible? Does compiler store some kind of map internally?

Solution

  • When you call a function template then all template arguments must either be specified explicitly or be deduced from the function arguments. When you call

    get<0>(test);
    

    Then Idx is 0, but there is no way for the compiler to know what T is supposed to be. The parameter is just Test, and T cannot be deduced from that.