I will pass "stack" and "queue" as template type in following codes.
Knowing that .front() and .top() is not the common function between "queue" and "stack", I use "if" to check.
But it still couldn't compile. (error C2039: 'top' is not the member of std::queue<_Ty>)
Is there any way to solve this problem?
template<typename T>
void push_pop(string *str, T *containers){
string s = typeid(containers).name();
if(s[11]=='q'){
str->push_back(containers->front()); containers->pop();
}else{
str->push_back(containers->top()); containers->pop();
}
}
I know I could use function overloading but I want to solve this using template.
Thanks for the answer!
The problem is that the compiler has to compile both branches of the check, and it cannot compile container.top() for a queue nor container.front() for a stack.
You have to implement separate function templates for std::queue and std::stack:
template<typename V, typename C>
void push_pop(std::string &str, std::stack<V, C> &container) {
str.push_back(container.top());
container.pop();
}
template<typename V, typename C>
void push_pop(std::string &str, std::queue<V, C> &container) {
str.push_back(container.front());
container.pop();
}
It is possible (and I would recommend it) to do this for only the relevant parts:
template<typename V, typename C>
V const &first_element(std::stack<V, C> const &container) {
return container.top();
}
template<typename V, typename C>
V const &first_element(std::queue<V, C> const &container) {
return container.front();
}
template<typename T>
void push_pop(std::string &str, T &container) {
str.push_back(first_element(container));
container.pop();
}
(untested) - I use a macro like the following:
#define FUNC_TEST(func) \
template<typename T> \
struct has_##func \
{ \
template <typename _1> static std::true_type test(decltype(&_1::func)*); \
template <typename > static std::false_type test(...); \
static const bool value = decltype(test<T>(0))::value; \
}
Then, for all the functions I need to test for, I would use the macro to generate the test code, for example:
FUNC_TEST(top);
FUNC_TEST(front);
Then, I would define several helper functions to handle the actual push back:
// If the type supports top(), then the first variant is enabled and selected
template <typename F>
inline enable_fn<has_top<F>, void> use_top(F* o, std::string* dest)
{ dest->push_back(o->top()); }
template <typename F>
inline disable_fn<has_top<F>, void> use_top(F*, std::string*)
{ }
// If the type supports front(), the first variant is selected from below automatically
template <typename F>
inline enable_fn<has_front<F>> use_front(F* o, std::string* dest)
{ dest->push_back(o->front()); }
template <typename F>
inline disable_fn<has_front<F>> use_front(F*, std::string*)
{ }
When calling, simply call both variants, and the compiler will select the appropriate action based on the SFINAE test.
template<typename T>
void push_pop(string *str, T *container){
use_top(container, str);
use_front(container, str);
container->pop();
}
It looks like we're calling both top() and front(), but depending on whether the container supports top() or front() one or the other function call will be compiled out. If the type T supports both front() and top(), it's a little problematic, but I'll leave that as an exercise for you to fix...
(NOTE: you'll need enable_fn which looks something like:
template <typename T>
using type_of = typename T::type;
template <typename C, typename R = void>
using enable_fn = type_of<std::enable_if<C::value, R>>;
I'll leave disable_fn as an exercise for you...
Problem of your code is that you are trying to make run-time check which results in requirement to container class provide both top() and front().
One approach to solve this is, as #Wintermute suggests, to use template specialization.
If you want more fancy and generic way, you can use SFINAE approach, which in combination with C++11 gives clean and nice looking code. Assuming we have only two possible kinds of containers - with either front() or top() method provided (adding new kinds of functions is pretty easy):
template<typename container>
auto get_from_container(container* c, int) -> decltype(std::declval<container>().front(), typename container::value_type()) {
return c->front();
}
template<typename container>
typename container::value_type get_from_container(container* c, long) {
return c->top();
}
template<typename T>
void push_pop(string *str, T *containers){
str->push_back(get_from_container(containers, 0)); containers->pop();
}
Notice second type argument in get_from_container - it is required to shadow "default" implementation with top() method (as described here)
Related
I'm trying to select at compile time between different template implementations depending on whether or not the argument implements a particular function. This is a common question (see this S.O. question and this example referenced by this article. The common answer is "use expression SFINAE".
Most of the examples show how expression SFINAE can be used to select based on the presence of a zero-argument function. I've tried to adapt these to my 1-argument use case by using declval (loosely based off of this example), but I cannot seem to get it to work.
I'm sure I'm doing something wrong in the example below, but I can't figure out what it is. The example is trying to define two versions of a template bool Util::Container::Contains(container, value) which will use the container's built-in find(value) method if it exists, and otherwise fall back to a linear search using std::find(...)
Please note: I know I can make this work by just overloading Contains() for unordered_map, unordered_set, etc., but instead I'd like to figure out this pattern-based approach so that it will automatically delegate to any container's find(value) without requiring an overload to be added.
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
namespace Util::Container {
namespace Detail
{
template <typename T>
class HasFindMethod
{
private:
typedef char YesType[1];
typedef char NoType[2];
// This is how the examples show it being done for a 0-arg function
//template <typename C> static YesType& Test(decltype(&C::find));
// Here's my attempt to make it match a 1-arg function
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>())));
template <typename C> static NoType& Test(...);
public:
enum { value = sizeof(Test<T>(0)) == sizeof(YesType) };
};
}
// Fallback: uses std::find() to do the lookup if no type-specific T::find(value) exists
template<typename T>
bool Contains(const T& in_container, const typename T::value_type& in_item)
{
const auto& result = std::find(in_container.cbegin(), in_container.cend(), in_item);
return (result != in_container.cend());
}
// Preferred: use T::find() to do the lookup if possible
template<typename T>
inline typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T& in_container, const typename T::value_type& in_item)
{
return (in_container.find(in_item) != in_container.end());
}
}
int main()
{
const std::vector<int> v { 1, 2, 3 };
const std::unordered_map<int, std::string> m { {1,"1" }, {2,"2"} };
const std::unordered_set<std::string> s { "1" , "2" };
// These should use the std::find()-based version of Contains() since vector and unordered_map
// have no find(value_type) method. And they do.
const bool r_v = Util::Container::Contains(v, 2);
const bool r_m = Util::Container::Contains(m, { 2, "2" });
// !!!!!!
//
// This should use the T::find(value_type)-based version of Contains() since
// unordered_set has a find(value_type) method.
//
// But it doesn't --- that's the issue I'm trying to solve.
//
const bool r_s = Util::Container::Contains(s, "2");
}
If anyone can show me how to fix this, I'd very much appreciate it.
FWIW, I'm attempting to implement this in Visual Studio 2017 v15.8
An easy way with decltype is
template<typename C, typename V>
auto Contains(const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto Contains(const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
but then when both function are possible you would have ambiguous call.
So just add extra parameter to prioritize the overload:
struct low_priority {};
struct high_priority : low_priority {};
template<typename C, typename V>
auto ContainsImpl(low_priority, const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto ContainsImpl(high_priority, const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
template <typename C, typename T>
auto Contains(const C& c, const T& t)
-> decltype(ContainsImpl(high_priority{}, c, t))
{
return ContainsImpl(high_priority{}, c, t);
}
Now about your version, you have several issues
The last one:
// Expected Fallback: uses std::find() to do the lookup if no type-specific T::find(value) exists
template<typename T>
bool Contains(const T&, const typename T::value_type&);
// Expected Preferred: use T::find() to do the lookup if possible
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
SFINAE allows to discard overload, but not to prioritize them.
You have to use priority, as shown above, or create exclusive set of overload:
template<typename T>
typename std::enable_if<!Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
In addition to that, as mentioned in comment map family would use key_type and not value_type.
Then your detection code is buggy,
// This is how the examples show it being done for a 0-arg function
//template static YesType& Test(decltype(&C::find));
No, this detects if C has a method find (without overload).
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>())));
Here, you use SFINAE, but final type would be the (const_)iterator, and Test<C>(0) won't take that overload (unless iterator can be build from 0 which is not the regular case). Adding extra * is a possibility, then you have pointer on iterator which might be initialized by 0.
Else you might use code provided in your provided link:
namespace detail{
template<class T, typename ... Args>
static auto test_find(int)
-> sfinae_true<decltype(std::declval<T>().find(std::declval<const Arg&>()...))>;
template<class, class ...>
static auto test_find(long) -> std::false_type;
} // detail::
template<class C, typename ... Args>
struct has_find : decltype(detail::test_find<T, Args...>(0)){};
// int has higher priority than long for overload resolution
and then use your traits with std::enable_if has_find<Container, Key>::value.
The immediate problem is that the argument you are passing to Test is not compatible with the YesType version.
For example, Detail::HasFindMethod<std::unordered_set<int>> will result in the following two Test signatures (because find would return an iterator):
static YesType& Test(std::unordered_set<int>::iterator);
static NoType& Test(...);
You try to call Test with the argument 0, which is not convertible to iterator. Hence, the second one is picked.
As a solution, use a pointer:
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>()))*);
// ^
Then do the check with a nullptr argument:
enum { value = sizeof(Test<T>(nullptr)) == sizeof(YesType) };
Now we would have ambiguity (the Test(...) would also match), so we can make that one a worse match:
template <typename C, class ... Args> static NoType& Test(void*, Args...);
As shown in the other answers, this is still a comparatively convoluted solution (and there are more issues that prevent it from working in your instance, such as ambiguity between the overloads when the enable_if does work). Just explaining the particular stopper in your attempt here.
A simpler (in my opinion) and more readable solution can be achieved using void_t utility:
template <typename T, typename Dummy = void>
struct has_member_find : std::false_type { };
template <typename T>
struct has_member_find<T,
std::void_t<decltype(std::declval<T>().find(std::declval<typename T::value_type &>()))>>
: std::true_type { };
template<typename T>
std::enable_if_t<!has_member_find<T>::value, bool>
Contains(const T& in_container, const typename T::value_type& in_item)
{
const auto& result = std::find(in_container.cbegin(), in_container.cend(), in_item);
return (result != in_container.cend());
}
template<typename T>
std::enable_if_t<has_member_find<T>::value, bool>
Contains(const T& in_container, const typename T::value_type& in_item)
{
return (in_container.find(in_item) != in_container.end());
}
Note that void_t is only available since C++17, however it you don't have a full C++17 support you can define it yourself since it's definition is ridiculously simple:
template< class... >
using void_t = void;
You can learn more about this utility and the pattern it introduces in this paper.
after having scrawled through several threads such as this one
SFINAE for detecting existence of non-member template function
without finding a solution, I am posting my question here. For a tiny logger class I want to surveil the amount of data logged using the << operator. When, for instance, a string is passe or an array having the .size() member function, I want to add size() number of bytes to that amount, sizeof(...) otherwise.
How can I (using VS 2010, which has very sparse C++11 support) make use of std::enable_if and std::is_member_function_pointer to reach my goal? How can I, for instance, define a function getSize() that operates as wanted?
The code below indicates what I want, but I guess I cannot define getSize twice...
Thanks a lot for help!
template<typename T,
typename = typename std::enable_if<std::is_member_function_pointer<decltype(&T::size)>::value>
>
size_t getSize(const T &p_rObj)
{
return p_rObj.size();
}
template<typename T,
typename = typename std::enable_if<!std::is_member_function_pointer<decltype(&T::size)>::value>
>
size_t getSize(const T &p_rObj)
{
return sizeof(p_rObj);
}
Addendumg/Edit: The following either does not work with VS 2010, which then expects every call of getSize(...) to have two parameters...
template<typename T>
size_t getSize(std::enable_if<std::is_member_function_pointer<decltype(&T::size)>::value>, const T &p_rObj)
{
return p_rObj.size();
}
template<typename T>
size_t getSize(std::enable_if<!std::is_member_function_pointer<decltype(&T::size)>::value>, const T &p_rObj)
{
return sizeof(p_rObj);
}
You define twice
template<typename T, typename> size_t getSize(const T &p_rObj);
And you forget ::type
and your traits is not SFINAE compatible
The traits could be implemented:
template <typename T> auto has_size_impl(int) -> decltype(std::declval<T>().size(), std::true_type{});
template <typename T> auto has_size_impl(...) -> std::false_type;
template <typename T>
using has_size = decltype(has_size_impl<T>(0));
You have to use std::enable_if in return type, template argument or regular argument:
template<typename T>
typename std::enable_if<has_size<T>::value, std::size_t>::type
getSize(const T &p_rObj);
or
template<typename T,
typename std::enable_if<has_size<T>::value>::type* = nullptr>
std::size_t getSize(const T &p_rObj);
or
template<typename T>
std::size_t getSize(const T &p_rObj,
typename std::enable_if<has_size<T>::value>::type* = nullptr);
Demo
Is there a way to compare the result of decltype in C++11?
In other words, why is this code invalid:
template<typename T, typename U>
void func(T& t, U& u) {
if(decltype(t) == decltype(u)) {
// Some optimised version for this case
} else {
// A more general case for differing types
}
}
I know that in some cases this particular problem can be solved by partial template specialisation; my question is about comparison of decltypes.
Edit: The question came up in the course of trying to provide defaults for free functions through SFINAE. Perhaps a better question would have been why this is invalid:
template<bool B>
bool SomeFunction() { ... }
template<typename T, typename U>
bool SomeFunctionWrapper(T& t, U& u) {
SomeFunction<decltype(t) == decltype(u)>();
}
I've since found another solution (that doesn't involve templates at all) but at one stage I tried this:
// If it exists, the free function is defined as
// bool AFreeFunction();
typedef struct { char } undefined;
template<typename T = void>
undefined AFreeFunction();
template<bool B>
bool AFreeFunctionWrapper_() {
return false;
}
template<>
bool AFreeFunctionWrapper_<false>() {
return AFreeFunction();
}
bool AFreeFunctionWrapper() {
return AFreeFunctionWrapper_<decltype(AFreeFunction()) == decltype(undefined)>();
}
I eventually got a variant of this strategy working with GCC 4.6, but then discovered that default template arguments are not allowed for template functions in MSVC, even in the 2012 RC. So the eventual solution looks like this:
class AFreeFunction {
public:
operator bool() { return false; }
};
If the function is defined, it gets called. If it's not, it is instead interpreted as a constructor for the class, which is then implicitly cast to bool.
You normally solve this problem through tag dispatching. Also, you already have the "declared type" of t and u - T& and U&, respectively. To compare types for equality, you can use std::is_same. However, overload resolution already solves this problem for you:
template<class T>
void f(T& v1, T& v2){ ... } // #1
template<class T, class U>
void f(T& t, U& u){ ... } // #2
#1 is more specialized than #2 if both arguments to f are of the same type. If you, for whatever reason, insist on solving this through manual type comparision, here's how it would look applying the before mentioned points:
#include <type_traits>
namespace detail{
template<class T, class U>
void f(T& t, U& u, std::true_type){ ... } // #1
template<class T, class U>
void f(T& t, U& u, std::false_type){ ... } // #2
} // detail::
template<class T, class U>
void f(T& t, U& u){
detail::f(t, u, std::is_same<T,U>()); // tag dispatching
}
std::is_same will derive from std::true_type if both types are the same, and from std::false_type if not.
Why is it invalid? The result of a decltype is, well, a type. So it's saying something like
if (int == int)
which the language obviously doesn't allow.
You'll need to separate the two parts of the function and put the specialized part inside a function in a specialized class, and forward the call there. It's painful.
Or, you could use typeid or run-time type information, if your implementation implements it correctly, although that will defer everything to when the program runs (which allows for fewer optimizations).
You can do this using SFINAE (std::enable_if):
template<typename T, typename U>
typename std::enable_if<std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "same\n";
}
template<typename T, typename U>
typename std::enable_if<!std::is_same<T, U>::value, void>::type func(T& t, U& u) {
std::cout << "different\n";
}
As Mehrdad says, decltype(t) and decltype(u) are types (T & and U & respectively), not values, so cannot be compared at value-expression level but must be compared at meta-expression (template) level.
I have is_callable trait defined like this:
#ifndef IS_CALLABLE_HPP
#define IS_CALLABLE_HPP
#include <type_traits>
namespace is_callable_detail
{
struct no {};
struct yes { no x[2]; };
template<bool CallableArgs, typename Callable, typename ReturnType, typename ...Args>
struct check_return
{
static const bool value = std::is_convertible<decltype(std::declval<Callable>()(std::declval<Args>()...)), ReturnType>::value;
};
template<typename Callable, typename ReturnType, typename ...Args>
struct check_return<false, Callable, ReturnType, Args...>
{
static const bool value = false;
};
}
template<typename Callable, typename Function>
struct is_callable;
template<typename Callable, typename ReturnType, typename ...Args>
struct is_callable<Callable, ReturnType(Args...)>
{
private:
template<typename T>
static is_callable_detail::yes check(decltype(std::declval<T>()(std::declval<Args>()...)) *);
template<typename T>
static is_callable_detail::no check(...);
static const bool value_args = sizeof(check<Callable>(nullptr)) == sizeof(is_callable_detail::yes);
static const bool value_return = is_callable_detail::check_return<value_args, Callable, ReturnType, Args...>::value;
public:
static const bool value = value_args && value_return;
};
#endif // IS_CALLABLE_HPP
My question is how to detect templated operator() which doesn't have arguments and has only return type T
template<typename T>
T operator()()
{
// ...
}
or
template<typename T, typename U>
auto operator()() -> decltype(std::declval<T>() + std::declval<U>())
{
// ...
}
I know that this situations are rare, but I wanted to ask is there any way to detect presence of templated operator() with no arguments and with one or more template arguments.
If you know in advance operator() is not going to be overloaded, you can try to take its address. If operator() is possibly overloaded, then a positive result would mean that there is an operator() present but a negative result would mean that either no operator() is present, or at least two overloads are.
Notice that a template will (as expected) bring several overloads of operator(). However, if you do know the number of template parameters that are not defaulted you can try taking the address of operator()<T> (for some type T that hopefully won't trigger SFINAE).
As a final note, I'd suggest not trying to spend too much time trying to inspect functors (or member functions, for the same reasons) without knowing what arguments to pass, just like what you already have. C++11 makes it very easy to write and use generic code that functions at the expression level.
You are trying to detect an operator() member function template with non-deduced template parameters, which isn't really "callable" at all, and also is kind of pointless - the function template should instead have a real name, because your example really misses the point of the whole operator thing. But let's solve your problem anyway.
Allow me to preface this with a plug for a library solution I'm working on, called CallableTraits (again, a work in progress).
While your case is not handled by CallableTraits, the library does employ a technique I'm about to describe to solve a very similar problem. The technique is a total hack, but it's standard compliant, and works for me on the following platforms:
GCC 5.2 and later
Clang 3.5 and later
Visual Studio 2015 Update 1 - basically works
Note: Visual Studio 2015 Update 2 is broken, because it incorrectly deduces std::index_sequence<I...> in partial specializations... I filed a bug report. See here for a description.
Note: If your standard library implementation doesn't have std::disjunction yet then you can use the sample implementation here instead.
I call the technique the template worm. It's the metaprogramming equivalent of spitting into a deep, dark well, just to hear how long it takes to splash.
What is a template worm?
A template worm is a class that is convertible to anything and everything.
Any operator expressions with a template worm operand will always evaluate to another template worm.
A template worm can only be used in an unevaluated context. In other words, you can only use it when a decltype surrounds the top-level expression, just like std::declval<T>().
A template worm wiggles itself into places it isn't supposed to be, and sticks to the first concrete type it can find. In a similar fashion, a real worm will stick to the concrete on any afternoon in July.
To solve your problem, we will start with no arguments, then recursively work up to an arbitrary limit of 10. We try to make the call to the (potential) function object by trying to pass the template worm by by function-style invocation, AND by template type argument (per your requirements).
This code doesn't account for INVOKE semantics, because that takes signifcantly more code. If you need this to work with pointers-to-member-functions and pointers-to-member-data, you can roll your own implementation for that.
I might not have covered all the operators, and I might not have implemented them all correctly, but you'll see the point.
One last thing:
I know of one catch. The return type can't depend on a dependent name (other than member operators).
Edit: Also, invocation/template instantiation needs to be SFINAE-friendly (i.e. no static_asserts).
Without further ado, here is your standalone solution (although you might wish you hadn't asked):
#include <utility>
#include <type_traits>
namespace detail {
//template_worm CANNOT be used in evaluated contexts
struct template_worm {
template<typename T>
operator T& () const;
template<typename T>
operator T && () const;
template_worm() = default;
#ifndef _MSC_VER
// MSVC doesn't like this... because it can deduce void?
// Whatever, we can do without it on Windows
template<typename... T>
template_worm(T&&...);
#endif //_MSC_VER
template_worm operator+() const;
template_worm operator-() const;
template_worm operator*() const;
template_worm operator&() const;
template_worm operator!() const;
template_worm operator~() const;
template_worm operator()(...) const;
};
#define TEMPLATE_WORM_BINARY_OPERATOR(...) \
\
template<typename T> \
constexpr inline auto \
__VA_ARGS__ (template_worm, T&&) -> template_worm { \
return template_worm{}; \
} \
\
template<typename T> \
constexpr inline auto \
__VA_ARGS__ (T&&, template_worm) -> template_worm { \
return template_worm{}; \
} \
\
constexpr inline auto \
__VA_ARGS__ (template_worm, template_worm) -> template_worm { \
return template_worm{}; \
} \
/**/
TEMPLATE_WORM_BINARY_OPERATOR(operator+)
TEMPLATE_WORM_BINARY_OPERATOR(operator-)
TEMPLATE_WORM_BINARY_OPERATOR(operator/)
TEMPLATE_WORM_BINARY_OPERATOR(operator*)
TEMPLATE_WORM_BINARY_OPERATOR(operator==)
TEMPLATE_WORM_BINARY_OPERATOR(operator!=)
TEMPLATE_WORM_BINARY_OPERATOR(operator&&)
TEMPLATE_WORM_BINARY_OPERATOR(operator||)
TEMPLATE_WORM_BINARY_OPERATOR(operator|)
TEMPLATE_WORM_BINARY_OPERATOR(operator&)
TEMPLATE_WORM_BINARY_OPERATOR(operator%)
TEMPLATE_WORM_BINARY_OPERATOR(operator,)
TEMPLATE_WORM_BINARY_OPERATOR(operator<<)
TEMPLATE_WORM_BINARY_OPERATOR(operator>>)
TEMPLATE_WORM_BINARY_OPERATOR(operator<)
TEMPLATE_WORM_BINARY_OPERATOR(operator>)
template<std::size_t Ignored>
using worm_arg = template_worm const &;
template<typename T>
struct success {};
struct substitution_failure {};
template<typename F, typename... Args>
struct invoke_test {
template<typename T, typename... Rgs>
auto operator()(T&& t, Rgs&&... rgs) const ->
success<decltype(std::declval<T&&>()(std::forward<Rgs>(rgs)...))>;
auto operator()(...) const->substitution_failure;
static constexpr int arg_count = sizeof...(Args);
};
// force_template_test doesn't exist in my library
// solution - it exists to please OP
template<typename... Args>
struct force_template_test {
template<typename T>
auto operator()(T&& t) const ->
success<decltype(std::declval<T&&>().template operator()<Args...>())>;
auto operator()(...) const->substitution_failure;
};
template<typename T, typename... Args>
struct try_invoke {
using test_1 = invoke_test<T, Args...>;
using invoke_result = decltype(test_1{}(
::std::declval<T>(),
::std::declval<Args>()...
));
using test_2 = force_template_test<Args...>;
using force_template_result = decltype(test_2{}(std::declval<T>()));
static constexpr bool value =
!std::is_same<invoke_result, substitution_failure>::value
|| !std::is_same<force_template_result, substitution_failure>::value;
static constexpr int arg_count = test_1::arg_count;
};
template<typename T>
struct try_invoke<T, void> {
using test = invoke_test<T>;
using result = decltype(test{}(::std::declval<T>()));
static constexpr bool value = !std::is_same<result, substitution_failure>::value;
static constexpr int arg_count = test::arg_count;
};
template<typename U, std::size_t Max, typename = int>
struct min_args;
struct sentinel {};
template<typename U, std::size_t Max>
struct min_args<U, Max, sentinel> {
static constexpr bool value = true;
static constexpr int arg_count = -1;
};
template<typename U, std::size_t Max, std::size_t... I>
struct min_args<U, Max, std::index_sequence<I...>> {
using next = typename std::conditional<
sizeof...(I)+1 <= Max,
std::make_index_sequence<sizeof...(I)+1>,
sentinel
>::type;
using result_type = std::disjunction<
try_invoke<U, worm_arg<I>...>,
min_args<U, Max, next>
>;
static constexpr bool value = result_type::value;
static constexpr int arg_count = result_type::arg_count;
};
template<typename U, std::size_t Max>
struct min_args<U, Max, void> {
using result_type = std::disjunction<
try_invoke<U, void>,
min_args<U, Max, std::make_index_sequence<1>>
>;
static constexpr int arg_count = result_type::arg_count;
static constexpr bool value = result_type::value;
};
template<typename T, std::size_t SearchLimit>
using min_arity = std::integral_constant<int,
min_args<T, SearchLimit, void>::arg_count>;
}
// Here you go.
template<typename T>
using is_callable = std::integral_constant<bool,
detail::min_arity<T, 10>::value >= 0>;
// This matches OP's first example.
struct Test1 {
template<typename T>
T operator()() {
return{};
}
};
// Yup, it's "callable", at least by OP's definition...
static_assert(is_callable<Test1>::value, "");
// This matches OP's second example.
struct Test2 {
template<typename T, typename U>
auto operator()() -> decltype(std::declval<T>() + std::declval<U>()) {
return{};
}
};
// Yup, it's "callable", at least by OP's definition...
static_assert(is_callable<Test2>::value, "");
// ints aren't callable, of course
static_assert(!is_callable<int>::value, "");
int main() {}
Okay, simple template question. Say I define my template class something like this:
template<typename T>
class foo {
public:
foo(T const& first, T const& second) : first(first), second(second) {}
template<typename C>
void bar(C& container, T const& baz) {
//...
}
private:
T first;
T second;
}
The question is about my bar function... I need it to be able to use a standard container of some sort, which is why I included the template/typename C part, to define that container type. But apparently that's not the right way to do it, since my test class then complains that:
error: 'bar' was not declared in this scope
So how would I go about implementing my bar function the proper way? That is, as a function of my template class, with an arbitrary container type... the rest of my template class works fine (has other functions that don't result in an error), it's just that one function that's problematic.
EDIT:
Okay, so the specific function (bar) is an eraseInRange function, that erases all elements in a specified range:
void eraseInRange(C& container, T const& firstElement, T const& secondElement) {...}
And an example of how it would be used would be:
eraseInRange(v, 7, 19);
where v is a vector in this case.
EDIT 2:
Silly me! I was supposed to declare the function outside of my class, not in it... pretty frustrating mistake to be making. Anyways, thanks everyone for the help, though the problem was a little different, the information did help me construct the function, since after finding my original problem, I did get some other pleasant errors. So thank you!
Traits solution.
Generalize not more than needed, and not less.
In some cases that solution might not be enough as it will match any template with such signature (e.g. shared_ptr), in which case you could make use of type_traits, very much like duck-typing (templates are duck typed in general).
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template<typename T>
struct has_const_iterator
{
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// bar() is defined for Containers that define const_iterator as well
// as value_type.
template <typename Container>
typename std::enable_if<has_const_iterator<Container>::value,
void>::type
bar(const Container &c, typename Container::value_type const & t)
{
// Note: no extra check needed for value_type, the check comes for
// free in the function signature already.
}
template <typename T>
class DoesNotHaveConstIterator {};
#include <vector>
int main () {
std::vector<float> c;
bar (c, 1.2f);
DoesNotHaveConstIterator<float> b;
bar (b, 1.2f); // correctly fails to compile
}
A good template usually does not artificially restrict the kind of types for which they are valid (why should they?). But imagine in the example above you need to have access to an objects const_iterator, then you can use SFINAE and type_traits to put those constraints on your function.
Or just to as the standard library does
Generalize not more than needed, and not less.
template <typename Iter>
void bar (Iter it, Iter end) {
for (; it!=end; ++it) { /*...*/ }
}
#include <vector>
int main () {
std::vector<float> c;
bar (c.begin(), c.end());
}
For more such examples, look into <algorithm>.
This approach's strength is its simplicity and is based on concepts like ForwardIterator. It will even work for arrays. If you want to report errors right in the signature, you can combine it with traits.
std containers with signature like std::vector (not recommended)
The simplest solution is approximated by Kerrek SB already, though it is invalid C++. The corrected variant goes like so:
#include <memory> // for std::allocator
template <template <typename, typename> class Container,
typename Value,
typename Allocator=std::allocator<Value> >
void bar(const Container<Value, Allocator> & c, const Value & t)
{
//
}
However: this will only work for containers that have exactly two template type arguments, so will fail miserably for std::map (thanks Luc Danton).
Any kind of secondary template arguments (not recommended)
The corrected version for any secondary parameter count is as follows:
#include <memory> // for std::allocator<>
template <template <typename, typename...> class Container,
typename Value,
typename... AddParams >
void bar(const Container<Value, AddParams...> & c, const Value & t)
{
//
}
template <typename T>
class OneParameterVector {};
#include <vector>
int main () {
OneParameterVector<float> b;
bar (b, 1.2f);
std::vector<float> c;
bar (c, 1.2f);
}
However: this will still fail for non-template containers (thanks Luc Danton).
Make the template templated on a template template parameter:
template <template <typename, typename...> class Container>
void bar(const Container<T> & c, const T & t)
{
//
}
If you don't have C++11, then you can't use variadic templates, and you have to provide as many template parameters as your container takes. For example, for a sequence container you might need two:
template <template <typename, typename> class Container, typename Alloc>
void bar(const Container<T, Alloc> & c, const T & t);
Or, if you only want to allow allocators which are themselves template instances:
template <template <typename, typename> class Container, template <typename> class Alloc>
void bar(const Container<T, Alloc<T> > & c, const T & t);
As I suggested in the comments, I would personally prefer to make the entire container a templated type and use traits to check if it's valid. Something like this:
template <typename Container>
typename std::enable_if<std::is_same<typename Container::value_type, T>::value, void>::type
bar(const Container & c, const T & t);
This is more flexible since the container can now be anything that exposes the value_type member type. More sophisticated traits for checking for member functions and iterators can be conceived of; for example, the pretty printer implements a few of those.
C++20 solution with Concepts and Ranges
In C++20, with the addition of Concepts and Ranges library, we can solve this simply with std::ranges::common_range:
void printContainer(const std::ranges::common_range auto & container);
{
for(const auto& item : container) std::cout << item;
}
Here, common_range is a concept that all stl containers satisfy. And you can get container's value type with:
std::ranges::range_value_t<decltype(container)>
You can also create your own container type that satisfy the concept with well defined iterator type and it begin() and it end() functions.
Alternatively, you can also use std::ranges::range, which has a bit less strict requirement than common_range, so it could allow more custom types.
Attempting to call the function with a non-satisfying type would give you error like template argument deduction/substitution failed: constraints not satisfied.
Here's the latest and expanded version of this answer and significant improvement over answer by Sabastian.
The idea is to define all traits of STL containers. Unfortunately, this gets tricky very fast and fortunately lot of people have worked on tuning this code. These traits are reusable so just copy and past below code in file called type_utils.hpp (feel free to change these names):
//put this in type_utils.hpp
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp
#include <type_traits>
#include <valarray>
namespace common_utils { namespace type_utils {
//from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
//also see https://gist.github.com/louisdx/1076849
namespace detail
{
// SFINAE type trait to detect whether T::const_iterator exists.
struct sfinae_base
{
using yes = char;
using no = yes[2];
};
template <typename T>
struct has_const_iterator : private sfinae_base
{
private:
template <typename C> static yes & test(typename C::const_iterator*);
template <typename C> static no & test(...);
public:
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
using type = T;
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
template <typename T>
struct has_begin_end : private sfinae_base
{
private:
template <typename C>
static yes & f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value>::type *);
template <typename C> static no & f(...);
template <typename C>
static yes & g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*);
template <typename C> static no & g(...);
public:
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
} // namespace detail
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template <typename T>
struct is_container : public std::integral_constant<bool,
detail::has_const_iterator<T>::value &&
detail::has_begin_end<T>::beg_value &&
detail::has_begin_end<T>::end_value> { };
template <typename T, std::size_t N>
struct is_container<T[N]> : std::true_type { };
template <std::size_t N>
struct is_container<char[N]> : std::false_type { };
template <typename T>
struct is_container<std::valarray<T>> : std::true_type { };
template <typename T1, typename T2>
struct is_container<std::pair<T1, T2>> : std::true_type { };
template <typename ...Args>
struct is_container<std::tuple<Args...>> : std::true_type { };
}} //namespace
#endif
Now you can use these traits to make sure our code only accepts container types. For example, you can implement append function that appends one vector to another like this:
#include "type_utils.hpp"
template<typename Container>
static typename std::enable_if<type_utils::is_container<Container>::value, void>::type
append(Container& to, const Container& from)
{
using std::begin;
using std::end;
to.insert(end(to), begin(from), end(from));
}
Notice that I'm using begin() and end() from std namespace just to be sure we have iterator behavior. For more explanation see my blog post.