The compiler doesn't complain when I do this ;-)
// Myfile.h
#include <iostream>
#include <vector>
namespace std
{
template<> class vector<int>
{
public:
vector ()
{
std::cout << "Happy Halloween !!!\n";
}
};
}
Is there any way to prevent this kind of undesirable specialization of a class/function template?
--EDIT--
I just used std:: as an example. What I'm looking for is a way to prevent this from happening to any template class.
What you do is specialize a standard library type inside a standard namespace.
Except for a few documented customization points (std::swap, std::hash<>) or specificly constrained specializations for User Defined Types (e.g. MySmartPtr<T>) this is against the specification and the result is undefined behaviour.
Edit: There is no mandatory diagnostic for this kind of rule violation.
To make it marginally harder for clients of your library to mess things up, you can do this trick:
namespace Public {
namespace Hidden { // DON'T TOUCH THESE!
template <typename> struct MyType { };
}
using Hidden::MyType;
}
Now, attempting to specialize MyType<> in namespace Hidden will fail.
No, the C++ language does not provide a general mechanism by which you can say "don't allow specializations of this template".
But it may not matter. For any instantiation that your code uses already, a user provided specialization will violate the one definition rule and their program may blow up in a fireball.
If you aren't using the instantiation in your library then what they do doesn't matter.
This is one of the cases where in C++ you simply can't prevent your user from shooting themself in the foot and if they choose to do so the responsibility is on them.
An alias template can not be specialized and has the behaviour you need for class templates.
template<class T>
struct my_class_implementation_which_should_not_be_used_directly
{
// impl
};
template<class T>
using my_class = my_class_implementation_which_should_not_be_used_directly<T>;
In addition you should document that specialising my_class_implementation_which_should_not_be_used_directly results in undefined behavior. Now your libraries user can not specialize my_class accidentally and is warned about the class with the ugly name directly.
Edit:
You can prevent specializations of your templates with enable_if in C++11. This is impractical though.
#include <type_traits>
template<
typename real_t,
typename = typename std::enable_if<
std::is_floating_point<real_t>::value
>::type
>
struct check_t;
template<
typename real_t,
typename = typename
std::enable_if<
std::is_floating_point<real_t>::value,
check_t<real_t>
>::type
>
class vec_t
{
};
#if 1
template<>
class vec_t<int> {};
template<>
class vec_t<int,check_t<int>> {};
#endif
void test()
{
vec_t<float> vecf;
vec_t<int> veci;
}
link
main.cpp:26:16: error: no type named 'type' in 'struct std::enable_if<false, void>'
The best way to prevent such behavior is through coding standards and code review.
You cannot force the compiler into an error in this case (other than using workarounds like those suggested in other answers), because that behavior is actually allowed by the language, although it's real usefulness can be questioned.
The matter is - it is very possible that you want/need to provide a generic behavior for multiple types (hence the use of templates) but you need to provide a specific implementation for some of the allowed types (notably std::string)
A quick & dirty example (it's a function, but the same could apply to classes) could be the following:
template<typename TData> TData GetData(std::string argument)
{
std::stringstream stream;
TData retVal;
stream.str(argument);
stream >> retVal;
return retVal;
}
However this is going to fail with std::string, as the >> operator would stop after the first blank space. So you could provide a dedicated specialization.
template<> std::string GetData(std::string argument)
{
return argument;
}
There are a few ways to go about this. You can declare specific specializations without defining them. eg:
template<> struct MyStruct<int>;
That way, if someone tries to instantiate with an int, they would get this specialization and it won't compile because there is n definition. Then you can write a simple macro to do this for all the types you don't want a specialization for.
For the inverse of this, make the definition empty:
template<typename T> struct MyStruct{};
Then define the specific types you plan to support:
template<> struct MyStruct<int> {...};
template<> struct MyStruct<std::string> {...};
//etc...
Related
Question may be weird so here is a brief motivational example:
#include <vector>
#include <type_traits>
template <typename T>
// workaround for gcc 8.3 where volatile int is not trivially copyable
using is_tc = std::is_trivially_copyable<std::remove_cv<T>>;
// static assert passes compile, oops
static_assert(is_tc<std::vector<int>>::value);
As you can see mistake is that I have passed the type trait itself to another type trait instead of passing ::type or using std::remove_cv_t.
Obvious solution is for me to not make mistakes, but I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits as arguments.
Now the hard thing is that there is a huge set of type traits in type_traits so IDK how would one go best about implementing this.
Note: I am not saying C++ should do this, I know it is a lot of work to prevent rare bugs, I am just trying to learn about more complicated concepts design where your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types(and that set includes the type you are restricting on).
Well, assuming you always need a ::type as argument where possible, here's a quick workaround:
template<class T> concept HasType = requires { typename T::type; };
template<class T> concept HasNoType = !HasType<T>;
template<HasNoType T> using remove_cv = std::remove_cv<T>;
template<HasNoType T> using remove_cv_t = typename remove_cv<T>::type;
Other than patching STL headers, or subclassing STL types (which is not always permitted), you cannot redefine what was predefined.
your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types
Whatever goes, you'll need a predicate to specify this set (operator ∊S for a given S). For instance has ++ is as good a predicate as any other.
The predicate can be refined with more levels of indirections and some boilerplate, say
template<class T> struct not_a_type_trait =
std::integral_constant<bool, HasNoType<T>> {};
template<class T> inline constexpr not_a_type_trait_v = not_a_type_trait<T>::value;
template<class T> concept NotATrait = not_a_type_trait_v<T>;
struct AnArg { using type = void; };
template<> struct not_a_type_trait<AnArg>: std::true_type {};
// now can be an arg to remove_cv
Or, here in this particular case, you can simply blacklist all the STL's traits, but that would be a really huge predicate to be updated with each Standard revision.
Concept: Is a TransformationTrait declared in the std namespace
I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits
Since metafunction traits are actually types themselves (which is kind of also the root of your problem), we can leverage this and construct a concept for T for whether Argument-Dependent Lookup (ADL) can find a smaller select set of STL-functions via ADL on an object of type T (in a non-evaluated context), where T may potentially be a metafunction trait; essentially an ADL-based (possibly brittle - see below) mechanism to query whether a given type T is defined in the std namespace or not, as opposed to the verbose approach of querying whether T is exactly one of numerous trait types defined in the std namespace.
If we combine this with the TransformationTrait requirement:
C++ named requirements: TransformationTrait
A TransformationTrait is a class template that provides a transformation of its template type parameter.
Requirements:
Takes one template type parameter (additional template parameters are optional and allowed)
The transformed type is a publicly accessible nested type named type
we can construct a common concept for a type T being a transformation trait in the std namespace. Note however that relying on ADL in this sense can be somewhat brittle, if for some reason a given project starts to declare functions which overloads function names from the STL. Expanding the smaller select set of STL-functions in the concept for possible ADL-lookup will make it harder to break from a non-std-implementor perspective.
E.g. defining a few concepts as follows:
namespace traits_concepts {
template <typename T>
concept FindsStlFunctionByAdlLookupOnT = requires(T t) {
// Rely on std::as_const being found by ADL on t, i.e.
// for t being an object of a type in namespace std.
as_const(t);
// If we are worried that a user may define an as_const
// function in another (reachable/found by lookup)
// namespace, expand the requirement with additional
// STL functions (that can be found via ADL).
move(t);
// ...
// Remember to add the appropriate includes.
};
template <typename T>
concept IsTransformationTrait = requires {
// REQ: The transformed type is a publicly accessible
// nested type named type.
typename T::type;
};
template <typename T>
concept IsStlTransformationTrait =
IsTransformationTrait<T> && FindsStlFunctionByAdlLookupOnT<T>;
template <typename T>
concept IsNotStlTransformationTrait = !IsStlTransformationTrait<T>;
} // namespace traits_concepts
Applied as:
namespace not_std {
template <traits_concepts::IsNotStlTransformationTrait T>
struct NotAnStlTrait {
using type = T;
};
struct Foo {};
}; // namespace not_std
// Is an STL transformation trait
static_assert(
traits_concepts::IsStlTransformationTrait<std::remove_cv<const int>>);
// Is not an STL transformation trait.
static_assert(
!traits_concepts::IsStlTransformationTrait<std::remove_cv_t<const int>>);
static_assert(
!traits_concepts::IsStlTransformationTrait<not_std::NotAnStlTrait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<not_std::Foo>);
int main() {}
And, for a custom trait added to std (assume for now that we are a compiler vendor; adding names to the std namespace is UB):
namespace std {
// Assume we are a compiler vendor.
// (Adding names to the std namespace is UB).
template <traits_concepts::IsNotStlTransformationTrait T>
struct custom_stl_trait {
using type = T;
};
}; // namespace std
static_assert(
traits_concepts::IsStlTransformationTrait<std::custom_stl_trait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<
std::custom_stl_trait<int>::type>);
int main() {}
DEMO.
I think that it is possible if all traits would have check for other traits, like all traits are inherited from _Trait, and do st is_base_of_v on its template parameter:
template<class T>
struct remove_cv : private _Trait
{
static_assert(!is_base_of_v<_Trait, T>, "Don't pass traits to traits");
using type = T;
};
If you want warning instead of hard error, this is harder. Need to make static_assert to evaluate as always true, but instantiating [[deprecated]] class for trait passed to trait.
Another simple solution would be to mark [[deprecated]] all traits that require ::type or ::value, deprecate them in favor of _t / _v. This is non-standard, but can be done under some preprocessor macro. or this deprecation can be made available by including a header that lists those deprecations.
The usual way to tag a set of type is to use a trait ;-)
template <class T>
struct is_trait : std::false_type {};
template <class T>
struct is_trait <is_trait <T>> : std:: true_type {};
template <class T>
inline constexpr auto is_trait_v = is_trait:: value;
I've encountered this problem while writing some library-like code recently, and I thought discussing it might help others as well.
Suppose I have a library with some function templates defined in a namespace. The function templates work on types supplied by client code, and their inner workings can be customized based on type traits defined for the client types. All client definitions are in other namespaces.
For the simplest example possible, a library function would basically have to look like this (note that all the code snippets are just wishful thinking, nothing compiles):
namespace lib
{
template<typename T> void f()
{
std::cout << traits_for<T>::str() << '\n'; //Use the traits in some way.
}
}
Client code would look like this:
namespace client
{
struct A { };
template<> std::string traits_for<A>::str() { return "trait value"; }
}
And then someone, somewhere could call
lib::f<client::A>();
and everything would magically work (the specialization of lib::f() would find the traits explicit specialization in the namespace where the template argument for T is declared, just like ADL does for functions and their arguments). The goal is to make it as easy as possible for client code to define those traits (there could be several) for each client class (there could be lots of those).
Let's see what we could do to make this work. The obvious thing is to define a traits class primary template in lib, and then explicitly specialize it for client types. But then clients can't define those explicit specializations in their own namespace; they have to exit it, at least up to the global namespace, define the explicit specialization, then re-enter the client namespace, which, for maximum fun, could be nested. I'd like to keep the trait definitions close to each client class definition, so this namespace juggling would have to be done near each class definition. Suddenly, a one-liner in client code has turned into a messy several-liner; not good.
To allow the traits to be defined in the client namespace, we could turn the traits class into a traits function, that could be called from lib like this:
traits_for(T())
but now we're creating an object of class T just to make ADL kick in. Such objects could be expensive to construct (or even impossible in some circumstances), so this isn't good either. We have to keep working with types only, not their instances.
Giving up and defining the traits as members of the client classes is not an option either.
Some plumbing required to make this work would be acceptable, as long as it doesn't complicate the definitions for each class and trait in the client namespace (write some code once, but not for every definition).
I've found a solution that satisfies these stringent requirements, and I'll write it up in an answer, but I'd like to find out what people think about this: alternatives, critique of my solution, comments about how all of this is either bleeding obvious or completely useless in practice, the works...
To find a declaration based on some argument, ADL looks like the most promising direction. So, we'll have to use something like
template<typename T> ??? traits_helper(T);
But we can't create objects of type T, so this function should only appear as an unevaluated operand; decltype springs to mind. Ideally, we shouldn't even assume anything about T's constructors, so std::declval could also be useful:
decltype(traits_helper(std::declval<T>()))
What could this do? Well, it could return the actual traits type if the helper would be declared like this:
template<typename T> traits_for<T> traits_helper(T);
We've just found a class template specialization in another namespace, based on the declaration of its argument.
EDIT: Based on a comment from Yakk, traits_helper() should take a T&&, to allow it to work if T's move constructor is not available (the function may not actually be called, but the semantic constraints required for calling it must be met). This is reflected in the complete sample below.
All put together in a standalone example, it looks like this:
#include <iostream>
#include <string>
#include <utility>
namespace lib
{
//Make the syntax nicer for library code.
template<typename T> using traits_for = decltype(traits_helper(std::declval<T>()));
template<typename T> void f()
{
std::cout << traits_for<T>::str() << '\n';
}
}
namespace client_1
{
//The following two lines are needed only once in every client namespace.
template<typename> struct traits_for { static std::string str(); };
template<typename T> traits_for<T> traits_helper(T&&); //No definition needed.
struct A { };
template<> std::string traits_for<A>::str() { return "trait value for client_1::A"; }
struct B { };
template<> std::string traits_for<B>::str() { return "trait value for client_1::B"; }
}
namespace client_2
{
//The following two lines are needed only once in every client namespace.
template<typename> struct traits_for { static std::string str(); };
template<typename T> traits_for<T> traits_helper(T&&); //No definition needed.
struct A { };
template<> std::string traits_for<A>::str() { return "trait value for client_2::A"; }
}
int main()
{
lib::f<client_1::A>(); //Prints 'trait value for client_1::A'.
lib::f<client_1::B>(); //Prints 'trait value for client_1::B'.
lib::f<client_2::A>(); //Prints 'trait value for client_2::A'.
}
Note that no objects of type T or traits_for<T> are created; the traits_helper specialization is never called - only its declaration is used.
What's wrong with just requiring clients to throw their specializations in the right namespace? If they want to use their own, they can:
namespace client
{
struct A { };
struct traits_for_A {
static std::string str() { return "trait value"; }
};
}
namespace lib
{
template <>
struct traits_for<client::A>
: client::traits_for_A
{ };
}
Could even give your users a macro if you don't want them to write all that out:
#define PROVIDE_TRAITS_FOR(cls, traits) \
namespace lib { \
template <> struct traits_for<cls> : traits { }; \
}
So the above can become
PROVIDE_TRAITS_FOR(client::A, client::traits_for_A)
ADL is awesome. Keep it simple:
namespace lib {
// helpers for client code:
template<class T>
struct default_traits{
using some_type=void;
};
struct no_traits{};
namespace details {
template<class T,class=void>
struct traits:lib::no_traits{};
template<class T>
struct traits<T,decltype(void(
traits_func((T*)0)
))>:decltype(
traits_func((T*)0)
){};
}
template<class T>
struct traits:details::traits<T>{};
}
Now simply add in the type Foo namespace:
namespace bob{
// use custom traits impl:
struct foo{};
struct foo_traits{
using some_type=int;
};
foo_traits traits_func(foo const*);
// use default traits impl:
struct bar {};
lib::default_traits<bar> traits_func(bar const*);
// use SFINAE test for any type `T`:
struct baz {};
template<class T>
std::enable_if_t<
std::is_base_of<T,baz>{},
lib::default_traits<T>
>
traits_func(T const*)
}
and we are done. Defining traits_func that takes a pointer convertable from foo* is enough to inject the trait.
If you fail to write such an overload, we get an empty traits, which is SFINAE friendly.
You can return lib::no_traits in an overload to explicitly turn off support, or just don;t write an overload that matches a type.
I have a c++ template class, which only operates correctly if the templatized type is plain old data. Anything with a constructor that does anything will not work correctly.
I'd like to somehow get a compiletime or runtime warning when someone tries to do so anyway.
//this should generate error
myclass<std::string> a;
//this should be fine
myclass<int> b;
is there a trick to do this?
#include <type_traits>
template<typename T>
class myclass
{
static_assert(std::is_pod<T>::value, "T must be POD");
// stuff here...
};
The above will cause a compilation error if you pass a non-POD type as the template parameter. This solution requires C++11 for the <type_traits> header and static_assert keyword.
EDIT: You can also implement this in C++03 if your compiler supports TR1 (most do):
#include <tr1/type_traits>
template<typename T>
class myclass
{
static char T_must_be_pod[std::tr1::is_pod<T>::value ? 1 : -1];
// stuff here...
};
If you have C++11 support std::is_pod should do exactly what you need. Use it with std::enable_if or with tag dispatch. For example something like this:
template <typename T, typename Enable = void>
class Test;
template<typename T>
class Test<T, typename std::enable_if<std::is_pod<T>::value, void>::type>
{};
int main() {
Test<int> t1;
//Test<std::string> t2; <-this will not compile
}
While the static_assert probably suffices in most cases, using enable_if and tag dispatch gives greater flexibility to the users of your class by the ways of SFINAE. Consider:
#include <type_traits>
#include <string>
#include <iostream>
template <typename T,
class=typename std::enable_if< std::is_pod<T>::value >::type>
struct myclass
{
typedef T value_type;
T data;
};
template <typename T>
void enjoy(T)
{
std::cout << "Enjoying T!" << std::endl;
}
template <typename T>
void enjoy(typename myclass<T>::value_type)
{
std::cout << "Enjoying myclass<T>::value_type!" << std::endl;
}
int main()
{
enjoy<int>(int()); // prints: Enjoying myclass<T>::value_type!
enjoy<std::string>(std::string()); // SFINAE at work - prints: enjoying T!
myclass<int> i; // compiles OK
//myclass<std::string> s; // won't compile - explicit instantiation w/non-POD!
}
Now if you remove the 2nd template argument from myclass definition, and instead, like others have suggested, add a
static_assert(std::is_pod<T>::value, "POD expected for T");
inside the class, the second line in main() will just fail to compile, triggering the static_assert.
That said, the errors from static_assert are much more friendly to human observer, than those from the failed enable_if. So, if static_assert works for you, go for it. Otherwise, if you do need to be friendlier to generic programming around your class, consider adding an explanatory comment around enable_if:
// POD expected for T
class=typename std::enable_if< std::is_pod<T>::value >::type>
unless everyone around you is C++11-fluent.
In real life, it's a good idea to explain why T must be POD both for static_assert and for the comment texts.
If you have not C++11
If the targeted POD types are limited (int, float, ...) You can put the implementation into a .cpp file and explicit instantiate it for that types:
.h file:
template <typename T>
class myclass
{
T data;
public:
void func();
};
.cpp file:
#include "myclass.h"
template <typename T>
void myclass<T>::func()
{
}
template class myclass<float>;
template class myclass<int>;
template class myclass<char>;
...
After that, myclass is just usable for those types and breaks for other.
Updating Simple's answer to newer C++20 standard changes, std::is_pod<T> will be deprecated. As this is most visible response in this topic in google, let me describe differences for others that will come here looking for up to date answer.
POD type was introduced as definition of Plain Old Data - equivalent of C structures. Requirements of POD since C++11 until C++20 are:
Is Trivial Type
Move/copy/default constructors are either trivial or deleted AND at least one of each exists.
The same for move/copy assignment operators
All members (also inherited) are trivial
Is Standard Layout Type
Has no members of reference type
Does not have virtual base class (non-virtual inheritance is allowed)
Does not have virtual functions
All members have the same access type (public/protected/private)
All members (also inherited) are standard layout types
For those of you who don't really know the difference between usages, here's the rule of thumb.
std::is_trivial should be checked when you plan to make memory copies/movements on your object. It guarantees that memory copy of this object will create exact copy, does not require construction or deconstruction. You can allocate memory and paste content received from socket. That's basic usage while transferring data over sockets or storing them in generic buffers.
std::is_standard_layout guarantees compatibility between different C++ standards as rules regarding memory alignment were changed over time and some implementations may use features guaranteed by one standard version which are relaxed on other one. Differences are related to memory ordering restrictions like each next member should have higher memory address or first member should have address of whole structure.
With type_traits, and static_assert, it is quite easy :
#include <type_traits>
struct A{
};
struct B{
virtual ~B(){}
};
template< class T >
struct MyClass
{
static_assert( std::is_pod<T>::value, "not a POD" );
};
int main()
{
MyClass<A> a;
//MyClass<B> b; -- break, cause not a POD
}
Sometimes when coding with C++ templates, you want to prevent users from instantiating a specific specialization or set of specializations, because the result would be nonsensical. So you can define a (specific or partial) specialization whose definition, if instantiated, would cause a compiler error. The goal would be, if a user "misuses" the template, to cause a compiler error right next to a comment in your header file explaining what not to do, rather than leaving the compiler to come up with some confusing error message by its own devices, or maybe allowing the questionable code to compile.
Example:
template <typename T> struct MyClassTemplate {
// ...
};
template <typename T> struct MyClassTemplate<T*> {
// Do not use MyClassTemplate with a pointer type!
typedef typename T::intentional_error err;
};
There are a number of ways to do this (depending on whether your specialization is a complete or partial specialization of a class or function). But the syntax used must (?) depend on a template parameter, or else the compiler will complain when it first parses the intentional-error definition. The example above has a hole in that somebody could stubbornly define an intentional_error nested type or member typedef (though I'd say they would then deserve whatever problems come up as a result). But if you use a trick too fancy, you're likely to get an indecipherable and/or misleading compiler error message, which mostly defeats the purpose.
Are there better straightforward ways to disallow template instantiations?
I'm aware that in C++0x, template Concepts and deleted function declarations will provide much better control over this sort of thing, but I'm looking for answers that are valid C++03.
You could just omit defining it.
template <typename T> struct MyClassTemplate<T*>;
You could also derive from a non-defined specialization
template <typename T> struct invalid;
template <typename T> struct MyClassTemplate<T*> : invalid<T> { };
Note that explicit specializations that declare classes or functions will never depend on template parameters. So, stuff like this that depend on template parameters can't work anyway. In that case, declaring a non-defined explicit specialization should be sufficient
template<> struct MyClassTemplate<int*>;
For me this sounds like a typical case for static_assert from C++0x or BOOST_STATIC_ASSERT. The static_assert functionality has the advantage that you can pass a custom error message so that the reason for the error is more clear.
Both ways are giving you the opportunity to prematurely end the compilation process under some custom defined compile time condition.
with static_assert:
template <typename T> struct MyClassTemplate<T*> {
static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!");
};
with BOOST_STATIC_ASSERT
template <typename T> struct MyClassTemplate<T*> {
// Do not use MyClassTemplate with a pointer type!
BOOST_STATIC_ASSERT(always_false<T>::value);
};
Always false would look something like this:
template< typename T >
struct always_false {
enum { value = false };
};
HTH
Edit: Fixed the examples to make them actually work ;-) Thanks to GMan!
If you don't want to use a library, this construct is pretty reliable (it's roughly what Boost does internally):
template <typename T>
void must_be_specialized(T const&)
{
enum dummy { d = (sizeof(struct must_be_specialized_for_this_type)
== sizeof(T)) };
}
You can put something analogous in a specialization to disallow instantiation of the template with that type. I wouldn't, personally, worry about must_be_specialized_for_this_type gaining a definition from somewhere, but you could use a forward declaration to squirrel it away in a private namespace if you really wanted.
Concepts were removed from '0x. You can use a library, like Boost Concept Check.
"Are there better straightforward ways to disallow template instantiations?" Nothing significantly better than what you have already identified. I am pretty sure C++ protection mechanisms are there to protect you from accident not from malice. And someone defining a specialisation or a class to break your intended use I would consider malicious. Perhaps you could hit the person in the back of the head each time they do it.
I personally prefer to put the checks into templates that exist only to describe the checks. That allows interesting combinations of inheritance and templates.
template <class T>
class not_with_pointer_t { };
template <class T>
class not_with_pointer_t<T*>;
template <class T>
class some_class_t : public not_with_pointer_t<T> { };
template <class T, template <class U> class base_t>
class another_class_t : public base_t<T> { };
typedef some_class_t<int> a_t; // ok
typedef some_class_t<void*> b_t; // error if instantiated
typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated
template <class T> class unrestricted_t { };
typedef another_class_t<void*, unrestricted_t> d_t; // ok
boost::enable_if
Consider the following function template:
template<typename T> void Foo(T)
{
// ...
}
Pass-by-value semantics make sense if T happens to be an integral type, or at least a type that's cheap to copy.
Using pass-by-[const]-reference semantics, on the other hand, makes more sense if T happens to be an expensive type to copy.
Let's assume for a second that you are writing a library. Ideally, as a library implementer, your job is to provide your consumers with a clean API that is both as generic and efficient as possible. How then, do you provide a generic interface that caters to both types of argument passing strategies?
Here is my first attempt at getting this to work:
#include <boost/type_traits.hpp>
template<typename T> struct DefaultCondition
{
enum {value = boost::is_integral<T>::value /* && <other trait(s)> */};
};
template< typename T, class Condition = DefaultCondition<T> > class Select
{
template<bool PassByValue = Condition::value, class Dummy = void> struct Resolve
{
typedef T type;
};
template<class Dummy> struct Resolve<false, Dummy>
{
typedef const T& type;
};
public: typedef typename Resolve<>::type type;
};
Typical usage:
template<typename T> class EnterpriseyObject
{
typedef typename Select<T>::type type;
public: explicit EnterpriseyObject(type)
{
// ...
}
};
struct CustomType {};
void Usage()
{
EnterpriseyObject<int>(0); // Pass-by-value.
(EnterpriseyObject<CustomType>(CustomType())); // Pass-by-const-reference.
}
This, of course, indirectly breaks implicit template argument deduction for non-class templates:
template<typename T> void Foo(typename Select<T>::type)
{
// ...
}
void Usage()
{
Foo(0); // Incomplete.
Foo<int>(0); // Fine.
}
This can be "fixed" with the Boost.Typeof library and a macro, a la the WinAPI:
#define Foo(Arg) ::Foo<BOOST_TYPEOF((Arg))>((Arg))
Though this is just a quasi-portable hack.
As you can see, my general approach is not really satisfactory for all cases.
As a hobbyist programmer, I neither have real-world experience nor do I have access to production-quality code for reference. I also realize that this might seem like a bad case of premature optimization, but I'm genuinely interested in a couple of things:
Do you, or have you used this type of optimization* in the past?
Does the Boost (or any other public) library already provide similar functionality?
If the answer to #1 or #2 is a 'yes' -- how is the non-class template case handled?
Are there any obvious pitfalls that I'm not seeing with something like this?
Finally, is this even a sane thing to do?
* Not profiled. ;)
Yes. All the time. I use it myself.
Yes, use Boost.Utility's Call Traits :)
Usage would be...
template <typename T>
void foo(boost::call_traits<T>::param_type param)
{
// Use param
}
As far as I know, non-class templates are passed-by-value unless it is faster to not. Thanks to partial template specialization, it can be customized relatively easily.
Sorry, didn't really read what you did, it just looked like exactly what I went through a few months ago. Therefore, can't really answer this one. My recommendation is just to read through Boost.Utility.
Of course!