C++ template specialization/overloading
Asked Answered
P

3

6

First of all, I'm sorry for the vague title of this question. I wasn't sure how to summarize it.

The thing I want to achieve is the following, I want to be able to pass template non-type parameters of different types to the same class-template, resulting in different instantiations. Something like this:

Foo<1>();
Foo<'1'>(); // different types of object

I don't think this is possible, therefore I'm forced to do something like this

template <typename T, T Val>
struct Foo;

template <int Val>
struct Foo<int, Val> 
{};

template <char Val>
struct Foo<char, Val>
{};

//...

Foo<int, 1>();
Foo<char, '1'>();

such that Foo can be specialized based upon the first template parameter. However this complicates the syntax of the mini-language I'm trying to implement in my metaprogramming framework. Is there any technique available that allows me to distinguish Foo<1> from Foo<'1'>? Basically what I want to do is set a compile-time flag (in an enum) to indicate wheter an int or char was passed, without explicitly specifying them.

EDIT The answers made me realize that my question implicates that I actually need (compile-time) instances of these objects. I don't...

Say that somehow the standard would permit me to overload a class template such that Foo<1> and Foo<'1'> are different types, and contain different values for their flag field. These types can then themselves be passed to another class template, which can inspect them and do interesting stuff with it, for example:

template <typename FooType>
struct Bar
{
    typedef typename If < FooType::flag, int, char >::Type Type;
};

This is all very easy to do when you have nothing against passing the type explicitly, but this seems superfluous...

Plano answered 10/1, 2014 at 16:24 Comment(8)
Huh, I never thought about this. +1Kiele
Neither did I, 'till I needed it ;-)Plano
This looks like a more general case of my question, whose answer I'm afraid waasn't encouragingSchnitzler
@Schnitzler You're right, not very encouraging.Plano
Would two templates one define as template<int Val> and one defined as template<char Val> work or would to compiler mutter about multiple definitions?Madonia
@Paranaix: Sorry I derped, previous comment removed. GCC says something about template parameters being redeclared.Plano
I dont know whether this works or compiles at all because VS doesnt support constexpr fully yet, but you could give it atleast a try with GCC and maybe the approach already helps you: pastebin.com/Y04gi3hu The idea is to exploit type deduction (what we actually want/need) but with constexpr functions.Madonia
@Paranaix You forgot to indicate trailing return type in your code.Mesa
M
2

You can use macro:

#define MAKE_FOO(value) \
    Foo<decltype(value), value>()

Actually, I think you need something like widespread function template make_something at compile-time. Unfortunately, I don't know how to implement it.

Mesa answered 10/1, 2014 at 16:48 Comment(4)
I was afraid of this being the only answer. Of course I already considered this option, but macro's are evil ;-) Will mark as answer if no-one has a better idea.Plano
Whats about the same approach but with default template parameters? template<typename T = decltype(Val), T Val> But somehow im sure this wont compile :DMadonia
@Paranaix it wouldn't work because default parameters need to be filled from the right.Hostetler
I ended up writing a macro to hide the ugly stuff from the end-user, even though this is very bad style... Marked as answer!Plano
H
1

If you simply need the values available at compile-time but don't actually need them as part of the type, (As in, if you don't need Foo<int, 1> and Foo<int, 2> to be different types) then you can use a constexpr constructor along with a constexpr function to give rise to Foo instances at compile-time.

#define DECLTYPE_AUTO(expr) \
  -> decltype(expr) { return expr; }

template <typename T>
struct Foo {

  constexpr Foo(T t) : value(t) {}

  T value;

};  // Foo

template <typename T>
constexpr auto MakeFoo(T val)
    DECLTYPE_AUTO(Foo<T>(val));

static_assert(MakeFoo(2).value == 2, "");
static_assert(MakeFoo('1').value == '1', "");

int main() {}
Hostetler answered 10/1, 2014 at 16:55 Comment(1)
Interesting... However I think it's not of much use to me. My fault entirely for not stating enough details in the question. Question edited :-)Plano
D
0

You could do something like this:

template <int Val>
struct Foo<int, Val> 
{
    static MyEnum tag = myTag1;
};


template <char Val>
struct Foo<char, Val>
{
    static MyEnum tag = myTag2;
};
Devastation answered 10/1, 2014 at 16:37 Comment(7)
And the purpose is what?Madonia
Distinguish Foo<1> from Foo<'1'>?Devastation
You still have to pass either int or char to Foo, which is exactly what I want to avoid. I want the compiler to deduce the type, like it does with function overloading. The post by Useless is about exactly the same problem, and it was then concluded that it's not possible...Plano
Sure it is not possible. Therefore I thought the above approach could help you with your idea and simplify your life.Devastation
This is exactly what I already did in my opening post. If that had been satisfactory, I wouldn't have asked in the first place.Plano
I got your question wrong. Your problem is instantiation of the instances not distinguishing them afterwards. Sorry.Devastation
@KitFisto Yup! No problem :-)Plano

© 2022 - 2024 — McMap. All rights reserved.