• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
CodeCriminal

std::enable_if, template specialization

15 posts in this topic

Hello all, been a while since I've posted here.
I've run into a problem for which my google-fu is not strong enough. [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

I am attempting to tighten some of my own template library code using the std::enable_if and others
within the <type_traits> header, here is a hypothetical (cut-down) situation below.

[CODE]
// firstsomething.hpp
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type func(const T& x) noexcept
{ return /* something */ }

// something.hpp
template <> typename std::enable_if<true, mytype>::type func(const mytype& x) noexcept;

// something.cpp
template <> typename std::enable_if<true, mytype>::type func(const mytype& x) noexcept
{ return /* return other something */ }
[/CODE]

Now, without the std::enable_if stuff and were I to replace this with just the bare types, the tests compiled and ran fine on GCC 4.7.1. However now I am having to wrestle with

error: template-id 'func<>' for 'std::enable_if<true, mytype>::type func(const mytype& x)' does not match any template declaration

So, where am I going wrong basically? [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

I did add 'template <typename T>' to the specializations instead of 'template <>' but doesn't this then mean the functions are no longer specializations of the original?

Thanks
0

Share this post


Link to post
Share on other sites
With some support code it does compile in VC10 as the correct headers are included at the necessary places. Are you sure that the file containing the specialization actually includes the definition of the template so that the compiler sees a template to specialize in the first place?

I don't have GCC available to try at the moment so cannot verify if that is actually the error message you get under that circumstance. Edited by Brother Bob
1

Share this post


Link to post
Share on other sites
Yeah just double checked and the header is being included. very odd because the error only crops up when I change the return type
of the template function to std::enable_if<...>::type
0

Share this post


Link to post
Share on other sites
This code compiles in GCC 4.6. I don't have 4.7 to test with.
[code]
#include <type_traits>
// firstsomething.hpp
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type func(const T& x) noexcept
{ return x; }
// something.hpp
typedef int mytype;
template <> typename std::enable_if<true, mytype>::type func(const mytype& x) noexcept { return 0; }
int main() {
func(0.0);
func(0);
}
[/code]

You could try changing the specialization to:
[code]
template <> typename std::enable_if<std::is_arithmetic<mytype>::value, mytype>::type func(const mytype& x) noexcept;
[/code]

If that doesn't work, you might not need a function specialization in the first place (I'm not sure what exactly you're trying to do), so you ought to be able to use function overloading:
[code]
mytype func(const mytype& x) noexcept;
[/code]

[quote]
I did add 'template <typename T>' to the specializations instead of 'template <>' but doesn't this then mean the functions are no longer specializations of the original?
[/quote]
Yes. You can't have a partial function specialization in C++ so all function specializations must start with "template<>" (as far as I know).
2

Share this post


Link to post
Share on other sites
EDIT: @sunder. I tried the little test example you gave which also compiled on gcc 4.7.1, however if you change mytype to a class instead of a simple typedef to one of the built in types, the problem I am having arises. here is what g++ had to say about it with code:

first.hpp
[CODE]
#ifndef __HEADER_FIRST_HPP__
#define __HEADER_FIRST_HPP__
#include <type_traits>
template <typename T> typename std::enable_if<std::is_arithmetic<T>::value, int>::type func(const T& x)
{ return x; }
#endif //__HEADER_FIRST_HPP__
[/CODE]

second.hpp
[CODE]
#ifndef __HEADER_SECOND_HPP__
#define __HEADER_SECOND_HPP__
#include "first.hpp"
class mytype { mytype( ) = default; };
template <> typename std::enable_if<true, int>::type func(const mytype& x)
{ return 0; }
#endif //__HEADER_SECOND_HPP__
[/CODE]

main.cpp
[CODE]
#include <iostream>
#include "second.hpp"
int main(int, char**)
{
std::cout << func(1.0) << std::endl;
std::cout << func(mytype( )) << std::endl;
return 0;
}
[/CODE]

[quote]In file included from main.cpp:2:0:
second.hpp:8:54: error: template-id 'func<mytype>' for 'std::enable_if<true, int>::type func(const mytype&)' does not match any template declaration
main.cpp: In function 'int main(int, char**)':
main.cpp:7:29: error: no matching function for call to 'func(mytype)'
main.cpp:7:29: note: candidate is:
In file included from second.hpp:4:0,
from main.cpp:2:
first.hpp:6:88: note: template<class T> typename std::enable_if<std::is_arithmetic<_Tp>::value, int>::type func(const T&)
first.hpp:6:88: note: template argument deduction/substitution failed:
first.hpp: In substitution of 'template<class T> typename std::enable_if<std::is_arithmetic<_Tp>::value, int>::type func(const T&) [with T = mytype]':
main.cpp:7:29: required from here
first.hpp:6:88: error: no type named 'type' in 'struct std::enable_if<false, int>'[/quote]

[quote name='sundersoft' timestamp='1346526447' post='4975513']
This code compiles in GCC 4.6. I don't have 4.7 to test with.
[code]
#include <type_traits>
// firstsomething.hpp
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type func(const T& x) noexcept
{ return x; }
// something.hpp
typedef int mytype;
template <> typename std::enable_if<true, mytype>::type func(const mytype& x) noexcept { return 0; }
int main() {
func(0.0);
func(0);
}
[/code]
[/quote]

I actually had the implementation of the specialization in a seperate .cpp file, perhaps this is my error. I'll give it a go [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

[quote]
You could try changing the specialization to:
[code]
template <> typename std::enable_if<std::is_arithmetic<mytype>::value, mytype>::type func(const mytype& x) noexcept;
[/code]
[/quote]

I could try this but it would involve specializing is_arithmetic for mytype which is something I don't really want to do. Even still it would only evaluate to true and produce the same problems.

[quote]
If that doesn't work, you might not need a function specialization in the first place (I'm not sure what exactly you're trying to do), so you ought to be able to use function overloading:
[code]
mytype func(const mytype& x) noexcept;
[/code]
[/quote]

Yeah, it occurred to me that the specialization may not be necessary in the first place and I could have just created an overload (which is what I have done and it seems to work). I think I was concerned that creating an overload would break func for integer types because mytype does not have an explicit constructor and it still takes a single argument integer value. However this is not the case apparently.

[quote][quote]
I did add 'template <typename T>' to the specializations instead of 'template <>' but doesn't this then mean the functions are no longer specializations of the original?
[/quote]
Yes. You can't have a partial function specialization in C++ so all function specializations must start with "template<>" (as far as I know).
[/quote]

I'm pretty sure partial specializations are allowed for functions, what I had done was not a partial specialization.


I think I would be more comfortable with a template specialization in the header than an overload because of the potential problem I spoke about above. Lets see if it works [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img] Edited by roadysix
0

Share this post


Link to post
Share on other sites
[quote name='roadysix' timestamp='1346530306' post='4975525']
Yeah, it occurred to me that the specialization may not be necessary in the first place and I could have just created an overload (which is what I have done and it seems to work). I think I was concerned that creating an overload would break func for integer types because mytype does not have an explicit constructor and it still takes a single argument integer value. However this is not the case apparently.
[/quote]
If you have a template and an overloaded function that takes an object that in turn takes a single integer in a non-explicit constructor, then the template is a better match and will be chosen over the function that requires an implicit conversion (the constructor call). The implicit conversion is, as required by the language, the deciding factor not to choose the overloaded function over the template which requires no implicit conversion. You are safe on that point (assuming, of course, there is nothing [i]preventing[/i] the template from being instantiated with the integer, in which case you may have problems).

[quote name='roadysix' timestamp='1346530306' post='4975525']
I'm pretty sure partial specializations are allowed for functions, what I had done was not a partial specialization.
[/quote]
No, functions cannot be partially specialized, only fully specialized. Overloading serves (most of) the purpose of partial specialization for classes. The reason you can partially specialize classes is because you cannot overload them.

[quote name='roadysix' timestamp='1346530306' post='4975525']
I think I would be more comfortable with a template specialization in the header than an overload because of the potential problem I spoke about above. Lets see if it works [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]
[/quote]
If the potential problem is the int-conversion you mentioned earlier, then that is not a problem as I responded to above. Edited by Brother Bob
1

Share this post


Link to post
Share on other sites
Ok, I guess the overload is my best bet for now seeing as the explicit specialization doesn't work, thanks Brother Bob, you learn something new everyday haha [img]http://public.gamedev.net//public/style_emoticons/default/smile.png[/img]

EDIT: I have a question about not being able to partially specialize a function as this is news to me.
lets say that I have a template function that takes any type performs some operations and then returns, pretty basic stuff

[CODE]
template <typename T> void function(const T x) { /* ... */ }
[/CODE]

Then I would like to "partially specialize" this function for pointers to T, I tried to create an overload like this:

[CODE]
template <typename T> void function(const T* x) { /* ... */ }
[/CODE]

I have never had to do anything like this before but that is besides the point. While this compiles, I did not get the expected result
and only the first template function was being called, even when I passed a pointer to the function! Which to me is surprising.

Without being able to partially specialize a function, what would be the correct way to go about setting up something like this?

Thanks Edited by roadysix
0

Share this post


Link to post
Share on other sites
That is not partial specialization, but regular function overloading. The two templates are separate.

I am not entirely sure about this explanation, but this is how I understand it and MSVC seems to support it as far as my testing goes at least. If you pass a pointer, then there has to be a mechanism to determine which one is called, of course, and the one with the best match is chosen. There are two things that are important:[list=1]
[*]A const value in a parameter is redundant and, at least as far as function prototype matching goes, equivalent to the const not being there.
[*]A non-const overload is preferred over a const overload for a non-const parameter.
[/list]
The meaning of point 1 is the following:
[code]
void foo(const int a);

int main()
{
int a = ...
foo(a);
}

void foo(int a)
{ ... }
[/code]
The forward declaration of foo with a const has exactly the same prototype, as far as the parameter matching goes, as the definition of foo.

So, you have two overloaded templates: (a) one with a value parameter, and (b) one with a const-pointer parameter. If you pass a non-const int pointer, overload (a) with T=int * is in fact preferred over (b) because of point 2 above. If you have a const int pointer, then overload (b) with T=int is preferred over (a) because it's const, and the pointer-type overload is a better match than a value overload with T=int *.

So what you have is the correct way, but I suspect you're not failing to call the pointer-overload because the other overload a better match, but because you're passing a non-const pointer and non-const overloads are preferred over const-overloads in that case. Edited by Brother Bob
1

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1346539070' post='4975572']
That is not partial specialization, but regular function overloading. The two templates are separate.
[/quote]

Haha I realized that, that is why I put "partial specialization" in inverted commas.

[quote name='Brother Bob' timestamp='1346539070' post='4975572']
So what you have is the correct way, but I suspect you're not failing to call the pointer-overload because it's a better match, but because you're passing a non-const pointer and non-const overloads are preferred over const-overloads in that case.
[/quote]

Ah yes, I passed a const pointer to the function and the correct function was called. Seems to me like this could be a dangerous game to play and partial specializations would be a better fit for this kind of thing.. though, I'm no c++ expert.

The same incorrect result is achieved if I have two overloads, one with a const-reference and another with a const-pointer when I try to pass in a non-const-pointer the function that takes a const-reference is called. I could create two overloads that do not take const parameters but what if I didn't want the function accidentally changing the value of that variable passed to it (of course I would use const) but what if I need to pass a non-const pointer to the function that accepts a const-pointer.. silent disaster. I could probably cast the non-const pointer to const but this isn't really desirable.

Of course, these are all hypothetical needs, and I don't imagine I will come across anything like this in the near future, if ever, though it is still interesting and worth knowing.
0

Share this post


Link to post
Share on other sites
[quote name='roadysix' timestamp='1346540294' post='4975578']
I could probably cast the non-const pointer to const but this isn't really desirable.
[/quote]
Overloading to the rescue (how ironic...):
[code]
template <typename T> void function(T x)
{ ... }

template <typename T> void function(const T* x)
{ ... }

template <typename T> void function(T* x)
{ function(static_cast<const T *>(x)); }
[/code]

[quote name='roadysix' timestamp='1346540294' post='4975578']
Of course, these are all hypothetical needs, and I don't imagine I will come across anything like this in the near future, if ever, though it is still interesting and worth knowing.
[/quote]
Perhaps not in a near future, but they certainly aren't hypothetical. I would say that your example with just an overload for pointer type is a fairly trivial and not totally unexpected example. The rules for determining parameter match to disambiguate a function call, and how templates and overloading interact, can be quite important to know once you start overloading functions even the slightest bit. Edited by Brother Bob
0

Share this post


Link to post
Share on other sites
[quote name='roadysix' timestamp='1346530306' post='4975525']
[quote]
You could try changing the specialization to:
[code]
template <> typename std::enable_if<std::is_arithmetic<mytype>::value, mytype>::type func(const mytype& x) noexcept;
[/code]
[/quote]

I could try this but it would involve specializing is_arithmetic for mytype which is something I don't really want to do. Even still it would only evaluate to true and produce the same problems.
[/quote]

I really doubt you're going to be able to specialize the function if your type doesn't have is_arithmetic<mytype>::value==true . You are allowed to specialize templates in the standard library (under certain conditions). The following code compiles even though the mytype specialization of func doesn't use is_arithmetic:

[code]
#include <iostream>
#include <type_traits>
// firstsomething.hpp
template <typename T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type func(const T& x) noexcept
{ return x; }
// something.hpp
class mytype {
};
namespace std {
template<> class is_arithmetic<mytype> {
static const bool value=1;
};
}
template <> mytype func(const mytype& x) noexcept { return mytype(); }
int main() {
func(0.0);
func(0);
}
[/code]

If the is_arithmetic specialization was removed, the code would fail to compile on GCC 4.6. If your type is not an arithmetic type then I think you're going to have to use function overloading.

[quote]I think I was concerned that creating an overload would break func for integer types because mytype does not have an explicit constructor and it still takes a single argument integer value. However this is not the case apparently.[/quote]
Yes; the compiler will prefer to instantiate a template over having a cast and using a non-template (although you can force it to use the non-template version by generating a function pointer to it and calling it, or you can force it to use a template by specifying the template arguments explicitly). Edited by sundersoft
0

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1346541495' post='4975584']
[quote name='roadysix' timestamp='1346540294' post='4975578']
I could probably cast the non-const pointer to const but this isn't really desirable.
[/quote]
Overloading to the rescue (how ironic...):
[code]
template <typename T> void function(T x)
{ ... }

template <typename T> void function(const T* x)
{ ... }

template <typename T> void function(T* x)
{ function(static_cast<const T *>(x)); }
[/code]
[/quote]

Incurring the overhead of a proxy function call is also undesirable, where simply being able to partially specialize a function would be favourable.
Alas, as pointed out it is not possible.

[quote name='Brother Bob' timestamp='1346541495' post='4975584']
[quote name='roadysix' timestamp='1346540294' post='4975578']
Of course, these are all hypothetical needs, and I don't imagine I will come across anything like this in the near future, if ever, though it is still interesting and worth knowing.
[/quote]
Perhaps not in a near future, but they certainly aren't hypothetical. I would say that your example with just an overload for pointer type is a fairly trivial and not totally unexpected example....
[/quote]

Hmm well I would still try to avoid this kind of thing some way or another.


[quote name='sundersoft' timestamp='1346542575' post='4975588']
[quote name='roadysix' timestamp='1346530306' post='4975525']
[quote]
You could try changing the specialization to:
[code]
template <> typename std::enable_if<std::is_arithmetic<mytype>::value, mytype>::type func(const mytype& x) noexcept;
[/code]
[/quote]
I could try this but it would involve specializing is_arithmetic for mytype which is something I don't really want to do. Even still it would only evaluate to true and produce the same problems.
[/quote]
I really doubt you're going to be able to specialize the function if your type doesn't have is_arithmetic<mytype>::value==true . You are allowed to specialize templates in the standard library (under certain conditions). The following code compiles even though the mytype specialization of func doesn't use is_arithmetic:
[/quote]

I soon realized this after posting that reply and decided to go with the function overload. Extending the standard namespace is something I try to avoid, but I am interested in what these "certain conditions" are, if you could explain further. Edited by roadysix
0

Share this post


Link to post
Share on other sites
[quote name='roadysix' timestamp='1346542823' post='4975590']
Incurring the overhead of a proxy function call is also undesirable
[/quote]
Any decent compiler will remove the extra function call.
0

Share this post


Link to post
Share on other sites
[quote name='Brother Bob' timestamp='1346543108' post='4975591']
[quote name='roadysix' timestamp='1346542823' post='4975590']
Incurring the overhead of a proxy function call is also undesirable
[/quote]
Any decent compiler will remove the extra function call.
[/quote]

Oh, thats good to know :) makes sense really
0

Share this post


Link to post
Share on other sites
[quote name='roadysix' timestamp='1346542823' post='4975590']
I soon realized this after posting that reply and decided to go with the function overload. Extending the standard namespace is something I try to avoid, but I am interested in what these "certain conditions" are, if you could explain further.
[/quote]

This is what the standard says about it (this is from a 2005 draft but I doubt the C++11 standard changed this significantly):

"It is unde?ned for a C++ program to add declarations or de?nitions to namespace std or namespaces within names-
pace std unless otherwise speci?ed. A program may add template specializations for any standard library template to
namespace std. Such a specialization (complete or partial) of a standard library template results in unde?ned behavior
unless the declaration depends on a user-de?ned type of external linkage and unless the specialization meets the standard
library requirements for the original template.
171)
A program may explicitly instantiate any templates in the standard
library only if the declaration depends on the name of a user-de?ned type of external linkage and the instantiation meets
the standard library requirements for the original template."

Footnote 171: "Any library code that instantiates other library templates must be prepared to work adequately with any user-supplied specialization that meets
the minimum requirements of the Standard."

You might want to wait for concepts to be standardized (or killed) before trying to add information about when a template would work to its interface. You're going to have to change your code if the standards comittee actually implements concepts and you want to use that feature (which would basically do what you're trying to do right now).
http://en.wikipedia.org/wiki/Concepts_%28C%2B%2B%29
However, this probably isn't going to be standardized for another 5 years at least (if it does become standard).
1

Share this post


Link to post
Share on other sites
[quote name='sundersoft' timestamp='1346546811' post='4975599']
You might want to wait for concepts to be standardized (or killed) before trying to add information about when a template would work to its interface. You're going to have to change your code if the standards comittee actually implements concepts and you want to use that feature (which would basically do what you're trying to do right now).
http://en.wikipedia..../Concepts_(C++)
However, this probably isn't going to be standardized for another 5 years at least (if it does become standard).
[/quote]

I had heard about concepts some time ago, though it is only now that I need them that I understand what they are for ("concepts" is a bit misleading), but as you said I'd be waiting a long time for them to implemented, (if the implementation of the c++11 standard is anything to go by) anyway thanks for clarifying and I think I'll stick with the regular function overload for now :)
0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0