Jump to content
  • Advertisement
Sign in to follow this  
Juliean

Template argument deduction fails here...

This topic is 744 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

 

very simple test case that showcases a problem I have:

template<typename Type>
struct Dummy
{
    using type = Type;
};

template<typename Type>
void dummyFunction(typename Dummy<Type>::type type)
{
}

void main(void)
{
    dummyFunction(1); // error: no instance of function "dummyFunction" fits argument list
    dummyFunction<int>(1); // compiles
}

The problem I have is that dummyFunction(1) won't compile, only when I explicitely specify the template type.

In practice, this is a more complex part of my RTT-system where "Dummy<Type>" would modify the signature of the type depending on what it receives, but this is as simple a case as I could reduce it to so you can test for yourself. "Just put the type yourself" is a lot of unnecessary overhead in my real use-case, so I would like to avoid it.

 

So my quest:

1) Why does template argument deduction fail here? I know it is due to using the Dummy::type, but why is this a problem?

2) Is there any way around this, that still lets me get away without having to put dummyFunction<Type>(X)? Or is this some limitation of template-metaprogramming that I was not aware of?  (Hopefully I didn't just forget an typename, then I'm going to kick something...)

 

EDIT: Well

Edited by Juliean

Share this post


Link to post
Share on other sites
Advertisement

Good point. You'd think the compiler would be smart enough to deduce the usage at compile time, but it doesn't. I'm no C++ savant, but if I had to guess in this sample, even the number literal 1 has some type ambiguation under the hood. I mean in C++ I can assign one (and have it be valid) in all kinds of scenarios, like:

char info = 1;
bool derp = 1;

Marcus Hansen

Edited by markypooch

Share this post


Link to post
Share on other sites
Is there more to the compiler error? Can you get the compiler to tell you what argument type it thinks it needs?

edit: Alas, ninja'd. Edited by Oberon_Command

Share this post


Link to post
Share on other sites
Assuming Dummy<Type>::type will always be Type, you can just do
 
template <typename Type>
void dummyFunction(Type type)
{
}

Share this post


Link to post
Share on other sites

You should probably make a separate traits-styled mechanic (using specialization) to do Type2Type lookup backwards too, that way your template function can lookup Type without having it as a template parameter directly.

Share this post


Link to post
Share on other sites

What you want then is for the compiler to find a type Type such that Dummy::type resolves to int. The type Type is unrelated to the parameter type int and the compiler would have to instantiate Dummy with every possible type in order to find the ones where Dummy::type is an int. That is, as you can imagine, a quite unreasonable task. There could be some obscure and hidden type, somewhere, that specialize the Dummy template with using Type = int and that would necessarily have to be a legal type for Type.

 

Ah, I didn't look at it form that way. Now that actually makes sense, thanks for the explanation.

 

Assuming Dummy::type will always be Type, you can just do

 

I was going to say that this is a wrong assumption. In my real use-case, Dummy::type could eigther be Type, const Type& or Type*, depending on what is passed to the function. But upon further inspection I realized that my usage of the this template here indeed is superflous. What I need it for is i.e. this:

variant.GetValue<int>() // returns int;
variant.GetValue<MyClass>() // returns MyClass*

This is done via a type-trait struct like in the example. Now in the cases where the user passes the value themselves this is not necessary, I can really just put template<typename Type> void function(Type value); and just check via static_assert that the attribute semantics match with what is expected for the type.

 

Thanks to all, helped a lot.
 

Share this post


Link to post
Share on other sites

Why not stick an "auto&&" in your function instead of the specific type, if you use a modern (C++14 compiler) that would work, and would also be easier to read.

Edited by NightCreature83

Share this post


Link to post
Share on other sites
Why not stick an "auto&&" in your function instead of the specific type, if you use a modern (C++14 compiler) that would work, and would also be easier to read.

 

As I said, the original idea was to enforce a certain semantic for a certain type. IE:

 

- int, float, bool are passed as value

- std::string, std::vector<>, user-defined structs are passed via const reference

- user-defined objects are passed via pointer

 

so the idea was, that if I called such a function, it would enforce the correct semantic automatically in the function signature:

int x = 1;
dummyFunction(x); // works
dummyFunction(&x); // doesn't compile, int is only passed by value

Object* pObject = new Object();
dummyFunction(pObject); // works
dummyFunction(*pObject); // doesn't compile, objects are only passed by pointer

So if I just put auto&&, it would take whatever I pass to it (like int***** as an extreme case), and I didn't want that. Apparently this isn't possible with the type-trait template that I made. So what I'm now going to do is akin to this:

template<typename Type>
void function(Type type)
{
    // clearType-trait removes const, reference, pointer
    static_assert(std::is_same<Type, typename semantic<typename clearType<Type>::type>::type>::value, "Type is passed with an invalid signature.");
}

This should make sure that the signature fits the expected value for the underlying type, and since all calls of those type eventually come down to my variant-class, I can just put it there and be fine with it, I assume.

Edited by Juliean

Share this post


Link to post
Share on other sites

Maybe overloading and SFINAE can help you. Something like this; seems to work on VS2015. Tweak the type trait checks as you like.

template <typename T>
using is_vector = std::is_same<T, std::vector< typename T::value_type, typename T::allocator_type>>;

template<typename T>
typename std::enable_if<std::is_arithmetic<T>::value>::type foo(T v)
{ std::cout << "value: " << v << std::endl; }

template<typename T>
typename std::enable_if<is_vector<T>::value>::type foo(T const &v)
{ std::cout << "vector: " << v[0] << std::endl; }

template<typename T>
typename std::enable_if<std::is_class<T>::value>::type foo(T *v)
{ std::cout << "pointer: " << v << std::endl; }

int main()
{
    int i = 42;
    double d = 3.14;
    std::vector<int> v{271};
    std::string s = "foo";

    foo(i);     /* ok; arithmetic type by value */
    foo(d);     /* ok; arithmetic type by value */
    foo(v);     /* ok; vector by const reference */
    foo(&v);    /* ok; pointer to class type although class happens to be vector */
    foo(&s);    /* ok; pointer to class type */

#if 0
    foo(&i);    /* not ok; pointer to non-class type */
    foo(s);     /* not ok; class type but not called as pointer */
#endif
}

The value function is enabled only for arithmetic types, the vector function is enabled only for vectors (of any kind) and the pointer function is enabled for classes only. Just be aware that you can pass vectors by pointer to the pointer function  since the vector is a class. There's no way to distinguish user defined classes from standard library classes, as far as I am aware, other than manually excluding them.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!