I have following code:
enum class type
{
zero,
one,
two
};
std::ostream& operator<<(std::ostream &os, type const &t)
{
switch(t)
{
case type::zero: os << "zero"; break;
case type::one: os << "one"; break;
case type::two: os << "two"; break;
default: os.setstate(std::ios_base::failbit);
}
return os;
}
template <typename K>
using pv = std::pair<type, std::vector<K>>;
template <typename K>
using ps = std::pair<type, K>;
template <typename B, typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
std::cout << std::endl;
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
rec<B>(j, f, t...);
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
auto [n, v] = t0;
for(auto k : v) {
rec<B>(j, f, s0, t..., ps<K>{n, k});
}
}
template <typename B, typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
auto [n, v] = t0;
for(auto k : v) {
rec<B>(j, f, t..., ps<K>{n, k});
}
}
void pro(int j, type t, std::string const &s)
{
std::cout << j << ":" << t << ":" << s << ", ";
}
int main()
{
std::vector<std::string> v0{"V00", "V01"};
std::vector<std::string> v1{"V10", "V11"};
std::vector<std::string> v2{"V20", "V21"};
int j = 0;
rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
return 0;
}
This compiles fine with clang++ (clang version 6.0.0-1ubuntu2) but fails with g++ (g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0). GCC throws the following error:
main.cpp: In function ‘int main()’:
main.cpp: error: call of overloaded ‘rec<char>(int&, void (&)(int, type, const string&), std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >, std::pair<type, std::vector<std::__cxx11::basic_string<char> > >)’ is ambiguous
rec<char>(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
^
main.cpp: note: candidate: void rec(int, F&, ps<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::vector<std::__cxx11::basic_string<char> >; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; ps<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
^~~
main.cpp: note: candidate: void rec(int, F&, pv<K>&, const T& ...) [with B = char; F = void(int, type, const std::__cxx11::basic_string<char>&); K = std::__cxx11::basic_string<char>; T = {std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >, std::pair<type, std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > >}; pv<K> = std::pair<type, std::vector<std::__cxx11::basic_string<char> > >]
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
^~~
Why are the functions at comment 1 and comment 3 ambiguous even though the parameter types pv and ps are different?
Interestingly, the following code works with both clang++ and g++ (note the removal of template parameter B from functions rec):
enum class type
{
zero,
one,
two
};
std::ostream& operator<<(std::ostream &os, type const &t)
{
switch(t)
{
case type::zero: os << "zero"; break;
case type::one: os << "one"; break;
case type::two: os << "two"; break;
default: os.setstate(std::ios_base::failbit);
}
return os;
}
template <typename K>
using pv = std::pair<type, std::vector<K>>;
template <typename K>
using ps = std::pair<type, K>;
template <typename F, typename K>
void rec(int j, F &f, ps<K> const &s0) // 4
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
std::cout << std::endl;
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, ps<K> const &s0, T const &... t) // 3
{
auto [n, v] = s0;
f(j, n, v);/*pro*/
rec(j, f, t...);
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, ps<K> const &s0, T const &... t) // 2
{
auto [n, v] = t0;
for(auto k : v) {
rec(j, f, s0, t..., ps<K>{n, k});
}
}
template <typename F, typename K, typename... T>
void rec(int j, F &f, pv<K> const &t0, T const &... t) // 1
{
auto [n, v] = t0;
for(auto k : v) {
rec(j, f, t..., ps<K>{n, k});
}
}
void pro(int j, type t, std::string const &s)
{
std::cout << j << ":" << t << ":" << s << ", ";
}
int main()
{
std::vector<std::string> v0{"V00", "V01"};
std::vector<std::string> v1{"V10", "V11"};
std::vector<std::string> v2{"V20", "V21"};
int j = 0;
rec(j, pro, pv<std::string>{type::zero, v0}, pv<std::string>{type::one, v1}, pv<std::string>{type::two, v2});
return 0;
}
Why does the second code work with g++ but not the first?
Related
I would like a function bool dominates(const std::tuple<T...>& t1, const std::tuple<T...>& t2) which returns true iff tuple t1 dominates tuple t2, i.e. for all i, t1[i] <= t2[i], in contrast with the default <= operator which uses a lexicographic comparison.
I've tried to adapt the answer from this question, but without success. It fails at compilation.
template<typename H>
bool& dominates_impl(bool& b, H&& h1, H&& h2)
{
b &= std::forward<H>(h1) <= std::forward<H>(h2);
return b;
}
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
{
b &= (std::forward<H>(h1) <= std::forward<H>(h2));
return dominates_impl(b, std::forward<T>(t1)..., std::forward<T>(t2)...);
}
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
bool b = true;
int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
(void)ctx;
return b;
}
template <typename ... T>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2)
{
return dominates(t1, t2, gen_indices<sizeof...(T)>{});
}
Compilation errors:
./common.hpp: In instantiation of 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&, integer_sequence<long unsigned int, I ...>) [with T = {long int, long int, long int, long int, long int}; long unsigned int ...I = {0, 1, 2, 3, 4}]':
./common.hpp:107:21: required from 'bool dominates(const std::tuple<_Tps ...>&, const std::tuple<_Tps ...>&) [with T = {long int, long int, long int, long int, long int}]'
examples.cpp:1624:65: required from here
./common.hpp:97:34: error: no matching function for call to 'dominates_impl(bool&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<0, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<1, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<2, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<3, std::tuple<long int, long int, long int, long int, long int> >&, std::__tuple_element_t<4, std::tuple<long int, long int, long int, long int, long int> >&)'
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:77:7: note: candidate: 'template<class H> bool& dominates_impl(bool&, H&&, H&&)'
77 | bool& dominates_impl(bool& b, H&& h1, H&& h2)
| ^~~~~~~~~~~~~~
./common.hpp:77:7: note: template argument deduction/substitution failed:
./common.hpp:97:34: note: candidate expects 3 arguments, 11 provided
97 | int ctx[] = { (dominates_impl(b, std::get<I>(t1)..., std::get<I>(t2)...), 0), 0};
| ~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate: 'bool& dominates_impl(bool&, H&&, H&&, T&& ..., T&& ...) [with H = const long int&; T = {const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&, const long int&}]'
84 | bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
| ^~~~~~~~~~~~~~
./common.hpp:84:7: note: candidate expects 19 arguments, 11 provided
The problem in you code is in dominates_impl()
template<typename H, typename... T>
bool& dominates_impl(bool& b, H&& h1, H&& h2, T&&... t1, T&&... t2)
you can't have two variadic argument list of argument in a function; you can have only one in last position.
But you don't need dominates_impl() at all: you can emulate C++17 template folding writing dominates() (the three argument version) as follows
template<typename... T, std::size_t... I>
bool dominates(
const std::tuple<T...>& t1,
const std::tuple<T...>& t2,
integer_sequence<std::size_t, I...>)
{
using unused = bool[];
bool b { true };
(void)unused { b, (b = b && std::get<I>(t1) <= std::get<I>(t2))... };
return b;
}
Remember to recover integer_sequence and gen_indices() from the original question.
I was able to make it work in C++11, but only by manually reinventing C++14's std::integer_sequence, and losing constexpr-ability:
#include <tuple>
#include <type_traits>
#include <assert.h>
template<typename T, T ...i> struct integer_sequence {};
template<typename T, T v=0>
struct counter {
static constexpr T n=v;
typedef counter<T, v-1> prev;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl;
template<typename T, T ...i>
struct integer_sequence_impl<T, counter<T>, i...> {
typedef struct integer_sequence<T, 0, i...> t;
};
template<typename T, typename V, T ...i> struct integer_sequence_impl
: integer_sequence_impl<T, typename V::prev, V::n, i...> {};
template<typename T, T n>
using create_integer_sequence=
typename integer_sequence_impl<T, counter<T, n-1>>::t;
template<class T, T N>
using make_integer_sequence=create_integer_sequence<T, N>;
template<typename T1,
typename T2,
std::size_t ...i>
bool dominates_impl(const T1 &t1,
const T2 &t2,
const integer_sequence<std::size_t, i...> &)
{
bool compare[]={
(std::get<i>(t1) <= std::get<i>(t2))...
};
for (auto f:compare)
if (!f)
return false;
return true;
}
template<typename ...T1,
typename ...T2,
typename=typename std::enable_if<sizeof...(T1) == sizeof...(T2)>::type>
bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
make_integer_sequence<std::size_t, sizeof...(T1)>
{});
}
int main()
{
assert(!dominates(std::tuple<int, int>{4, 2},
std::tuple<int, int>{3, 1}));
assert(dominates(std::tuple<int, int>{2, 2},
std::tuple<int, int>{3, 2}));
return 0;
}
A good chunk of the above is a half-baked std::integer_sequence. With that, and C++17's fold expressions this becomes a no-brainer:
#include <tuple>
#include <type_traits>
template<typename T1,
typename T2,
std::size_t ...i>
constexpr bool dominates_impl(const T1 &t1,
const T2 &t2,
const std::integer_sequence<std::size_t, i...> &)
{
return ( (std::get<i>(t1) <= std::get<i>(t2)) && ...);
}
template<typename ...T1,
typename ...T2,
typename=std::enable_if_t<sizeof...(T1) == sizeof...(T2)>>
constexpr bool dominates(const std::tuple<T1...> &t1,
const std::tuple<T2...> &t2)
{
return dominates_impl(t1, t2,
std::make_index_sequence<sizeof...(T1)>{});
}
static_assert(!dominates(std::tuple{4, 2},
std::tuple{3, 1}));
static_assert(dominates(std::tuple{2, 2},
std::tuple{3, 2}));
I'm trying to create a custom type of class object and i would i like to add to a vector.
how it's done?
this is the code of the class and the issues it trows at me.
the class on it's own works as expected, but when trying to add these to a vector!
edit: uploaded the total code with all the comments and stuff.
#ifndef TRIPLE_H
#define TRIPLE_H
#include <iostream>
#include <sstream>
#include <initializer_list>
// https://en.cppreference.com/w/cpp/memory/unique_ptr
#include <memory>
using namespace std;
template <typename S, typename T, typename R>
class Triple
{
public:
Triple();
Triple(S, T, R);
Triple(Triple &);
~Triple();
const Triple &operator=(const Triple &other);
//bool operator> (const TRIPLE &lhs, const TRIPLE &rhs);
//bool operator> (const TRIPLE &lhs);
//bool operator>(const TRIPLE &right) const;
string toString();
string getPerimeter();
int getPeri();
S* getFirst();
S getFirstvalue(); ///////////////
T* getSecond();
void set(S,T,R);
int getPairCount();
void setFirst(S);
void setSecond(T);
private:
S *first;
T *second;
R *third;
int *perimeter;
static int count_of_triples;
};
template <typename S, typename T, typename R>
int Triple<S, T, R>::count_of_triples=0;
// 0-parameter constructor
template <typename S, typename T, typename R>
Triple<S, T, R>::Triple() : first(NULL), second(NULL), third(NULL), perimeter(0)
{
//f = NULL;
//s = NULL;
count_of_triples++;
}
// 2-param constructor
template <typename S, typename T, typename R>
Triple<S, T, R>::Triple(S x, T y, R z) // : first(x), second(y), third(z), perimeter(x + y + z)
{
first = new S;
*first = x;
second = new T;
*second = y;
third = new R;
*third= y;
perimeter = new int;
//*perimeter = z;
//std::cout << "perimeter: " << (x + y + z) << end;
*perimeter = (x + y + z);
count_of_triples++;
}
template <typename S, typename T, typename R>
S Triple<S, T, R>::getFirstvalue(){
if (first!=NULL){
// if(*first == 0){
// std::cout << "error" << endl;
// }
// else{
// std::cout << "all ok" << endl;
// }
return *first;
}
else
//return NULL; // warning: converting to non-pointer type 'int' from NULL
return 0;
}
template <typename S, typename T, typename R>
string Triple<S, T, R>::toString()
{
stringstream ss;
ss<<"(";
if (first==NULL)
ss<<"NULL";
else
ss<<(*first);
ss<<",";
if (second==NULL)
ss<<"NULL";
else
ss<<(*second);
ss<<",";
if (third==NULL)
ss<<"NULL";
else
ss<<(*third);
ss<<")";
return ss.str();
}
template <typename S, typename T, typename R>
string Triple<S, T, R> ::getPerimeter(){
stringstream ss;
ss<<"(";
if (perimeter==NULL)
ss<<"NULL";
else
ss<<(*perimeter);
ss<<")";
return ss.str();
}
template <typename S, typename T, typename R>
int Triple<S, T, R> ::getPeri(){
return *perimeter;
}
template <typename S, typename T, typename R>
Triple<S, T, R> ::~Triple()
{
if(first != NULL)
delete first;
if(second != NULL)
delete second;
if(third != NULL)
delete third;
if(perimeter != NULL)
delete perimeter;
first = NULL;
second = NULL;
third = NULL;
perimeter = NULL;
count_of_triples--;
}
template <typename S, typename T, typename R>
int Triple<S, T, R>::getPairCount(){
return count_of_triples;
}
template <typename S, typename T, typename R>
void Triple<S, T, R>::set(S x, T y, R z){
if (first ==__null)
first = new S;
*first = x;
std::cout << "first:" << *first << endl;
if (second ==__null)
second = new S;
*second = y;
std::cout << "second:" << *second << endl;
if (third ==__null)
third = new R;
*third = z;
std::cout << "third:" << *third << endl;
if (perimeter ==__null)
perimeter = new int;
*perimeter = (x + y + z);
}
template <typename S, typename T, typename R>
const Triple<S, T, R> &Triple<S, T, R>::operator=(const Triple<S, T, R> &other){
}
template <typename S, typename T, typename R>
bool operator>(Triple<S, T, R> &lhs, Triple<S, T, R> &rhs){
return lhs.getPeri() > rhs.getPeri();
}
template <typename S, typename T, typename R>
bool operator<(Triple<S, T, R> &lhs, Triple<S, T, R> &rhs){
return lhs.getPeri() < rhs.getPeri();
}
#endif // TRIPLE_H
main:
#include <iostream>
#include <vector>
#include <algorithm>
//#include <utility>
#include "Triple.h"
typedef vector<Triple<int, int, int>> triVector;
int main(){
Triple<int, int, int> triple;// {1,2,3};
triple.set(1,2,3);
std::cout << " ***** " << triple.toString() << endl;
std::cout << "get perimeter " << triple.getPerimeter() << endl;
std::cout << "get count " << triple.getPairCount() << endl;
Triple<int, int, int> *triple2 = new Triple<int, int, int>(1,2,5);
//TRIPLE<int, int, int> *triple2 = new TRIPLE<int, int, int>({1,2,1});
std::cout << " ***** " << triple2->toString() << endl;
std::cout << "get perimeter " << triple2->getPerimeter() << endl;
std::cout << "get count " << triple2->getPairCount() << endl;
Triple<int, int, int> triple3;// {1,2,3};
triple3.set(1,2,2);
if(triple < *triple2){
std::cout << "true" << endl;
}
else{
std::cout << "not true" << endl;
}
std::cout << triple.getFirstvalue() << endl;
triVector temp;
temp.push_back(*triple2);
// temp.push_back(triple);
// temp.push_back(triple3);
for(auto i : temp){
std::cout << "peri: " << i.getPeri() << endl;
}
std::cout << "-----------------------------------------" << endl;
//sort(temp.begin(), temp.end());
// for(auto i : temp){
// std::cout << "peri: " << i.getPeri() << endl;
// }
// std::cout << "-----------------------------------------" << endl;
return 0;
}
error:
/usr/include/c++/9/ext/new_allocator.h:145: error: binding reference of type ‘Triple<int, int, int>&’ to ‘const Triple<int, int, int>’ discards qualifiers
In file included from /usr/include/x86_64-linux-gnu/c++/9/bits/c++allocator.h:33,
from /usr/include/c++/9/bits/allocator.h:46,
from /usr/include/c++/9/string:41,
from /usr/include/c++/9/bits/locale_classes.h:40,
from /usr/include/c++/9/bits/ios_base.h:41,
from /usr/include/c++/9/ios:42,
from /usr/include/c++/9/ostream:38,
from /usr/include/c++/9/iostream:39,
from ../Pair_template/main.cpp:14:
/usr/include/c++/9/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = Triple<int, int, int>; _Args = {const Triple<int, int, int>&}; _Tp = Triple<int, int, int>]’:
/usr/include/c++/9/bits/alloc_traits.h:482:2: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = Triple<int, int, int>; _Args = {const Triple<int, int, int>&}; _Tp = Triple<int, int, int>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<Triple<int, int, int> >]’
/usr/include/c++/9/bits/stl_vector.h:1189:30: required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = Triple<int, int, int>; _Alloc = std::allocator<Triple<int, int, int> >; std::vector<_Tp, _Alloc>::value_type = Triple<int, int, int>]’
../Pair_template/main.cpp:91:28: required from here
/usr/include/c++/9/ext/new_allocator.h:145:20: error: binding reference of type ‘Triple<int, int, int>&’ to ‘const Triple<int, int, int>’ discards qualifiers
145 | noexcept(noexcept(::new((void *)__p)
| ^~~~~~~~~~~~~~~~~~
146 | _Up(std::forward<_Args>(__args)...)))
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Your declaration of the class is wrong:
Triple(Triple &) ~Triple();
Should be:
TRIPLE(TRIPLE&);
~TRIPLE();
Your class is very weird and seems to be very heavily inspired by Java. No need for all those pointers and the standard std::tuple is probably a good fit for what you're trying to do .
the credit goes to #PaulMcKenzie
#include <iostream>
#include <vector>
#include <tuple>
#include <string>
#include <sstream>
template <typename S, typename T, typename R>
class TRIPLE
{
public:
TRIPLE();
TRIPLE(S, T, R);
void set(S, T, R);
S getFirstvalue();
std::string toString();
private:
std::tuple<S, T, R> m_tuple;
};
// 0-parameter constructor
template <typename S, typename T, typename R>
TRIPLE<S, T, R>::TRIPLE() : m_tuple{} {}
// 3-param constructor
template <typename S, typename T, typename R>
TRIPLE<S, T, R>::TRIPLE(S x, T y, R z) : m_tuple{x, y, x} {}
template <typename S, typename T, typename R>
void TRIPLE<S, T, R>::set(S x, T y, R z)
{
m_tuple = {x, y, z};
}
template <typename S, typename T, typename R>
S TRIPLE<S, T, R>::getFirstvalue()
{
return std::get<0>(m_tuple);
}
template <typename S, typename T, typename R>
std::string TRIPLE<S, T, R>::toString()
{
std::stringstream ss;
ss << "(" << std::get<0>(m_tuple) << "," << std::get<1>(m_tuple)
<< "," << std::get<2>(m_tuple) << ")";
return ss.str();
}
typedef std::vector<TRIPLE<int, int, int>> triVector;
int main()
{
TRIPLE<int, int, int> triple;
triple.set(1, 2, 3);
TRIPLE<int, int, int> triple2;
triple2.set(1, 2, 3);
TRIPLE<int, int, int> triple3;
triple3.set(1, 2, 3);
TRIPLE<int, int, int> triple4;
triple4.set(1, 2, 3);
triVector temp;
temp.push_back(triple2);
temp.push_back(triple);
temp.push_back(triple3);
for (auto i : temp)
{
std::cout << "first value: " << i.getFirstvalue() << std::endl;
}
return 0;
}
I'm writing a function that can pour the content of a string vector into variables. Here's what it looks like:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
template <typename T, int N = 0>
void pour2(std::vector<std::string> const& vals, T& val) {
std::stringstream ss;
ss << vals[N];
ss >> val;
}
template <typename T, typename ...Ts, int N = 0>
void pour2(std::vector<std::string> const& vals, T& val, Ts& ...args) {
std::stringstream ss;
ss << vals[N];
ss >> val;
pour2<Ts..., N+1>(vals, args...);
}
int main() {
std::vector<std::string> info = {"3", "1.5", "/home/tq/playground/"};
int sz;
double val;
std::string dir;
pour2(info, sz, val, dir);
std::cout << "size = " << sz << std::endl;
std::cout << "value = " << val << std::endl;
std::cout << "dir = " << dir << std::endl;
return 0;
}
However, g++-9.2 complains that
test.cpp: In instantiation of ‘void pour2(const std::vector<std::__cxx11::basic_string<char> >&, T&, Ts& ...) [with T = int; Ts = {double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}; int N = 0]’:
test.cpp:30:26: required from here
test.cpp:18:19: error: no matching function for call to ‘pour2<double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, (0 + 1)>(const std::vector<std::__cxx11::basic_string<char> >&, double&, std::__cxx11::basic_string<char>&)’
18 | pour2<Ts..., N+1>(vals, args...);
| ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
test.cpp:7:6: note: candidate: ‘template<class T, int N> void pour2(const std::vector<std::__cxx11::basic_string<char> >&, T&)’
7 | void pour2(std::vector<std::string> const& vals, T& val) {
| ^~~~~
test.cpp:7:6: note: template argument deduction/substitution failed:
test.cpp:18:19: error: wrong number of template arguments (3, should be at least 1)
18 | pour2<Ts..., N+1>(vals, args...);
| ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
test.cpp:14:6: note: candidate: ‘template<class T, class ... Ts, int N> void pour2(const std::vector<std::__cxx11::basic_string<char> >&, T&, Ts& ...)’
14 | void pour2(std::vector<std::string> const& vals, T& val, Ts& ...args) {
| ^~~~~
test.cpp:14:6: note: template argument deduction/substitution failed:
and clang-9.0.1 says
test.cpp:18:2: error: no matching function for call to 'pour2'
pour2<Ts..., N+1>(vals, args...);
^~~~~~~~~~~~~~~~~
test.cpp:30:2: note: in instantiation of function template specialization 'pour2<int, double, std::__cxx11::basic_string<char> , 0>' requested here
pour2(info, sz, val, dir);
^
test.cpp:14:6: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Ts'
void pour2(std::vector<std::string> const& vals, T& val, Ts& ...args) {
^
test.cpp:7:6: note: candidate function template not viable: requires 2 arguments, but 3 were provided
void pour2(std::vector<std::string> const& vals, T& val) {
^
1 error generated.
I find that if I move the non-type template parameter to be the first argument, the code can compile and work as expected:
template <int N = 0, typename T>
void pour(std::vector<std::string> const& vals, T& val) {
std::stringstream ss;
ss << vals[N];
ss >> val;
}
template <int N = 0, typename T, typename ...Ts>
void pour(std::vector<std::string> const& vals, T& val, Ts& ...args) {
std::stringstream ss;
ss << vals[N];
ss >> val;
pour<N+1, Ts...>(vals, args...);
}
I just wonder, why it doesn't work in the first case?
If N appears after the parameter pack, N needs to be deduced from the function arguments or have default arguments, and N cannot be deduced by the functions argument.
"In a function template, the template parameter pack may appear earlier in the list provided that all following parameters can be deduced from the function arguments, or have default arguments". - [parameter_pack - cppreference]
I want to "filter" a parameter pack of the variadic template function like in the following code (need to "filter" only variables of certain types):
#include <iostream>
#include <utility>
#include <cstdlib>
struct Z {};
struct test
{
using result_type = void;
template< typename... P >
result_type apply_filter(P &&... _p) const
{
using std::forward;
return operator () (forward< P >(_p)...);
}
template< typename... T >
result_type operator () (std::string const & _s, T &&... _tail) const
{
std::cout << _s << std::endl;
return operator () (std::forward< T >(_tail)...);
}
template< typename... T >
result_type operator () (double const & _x, T &&... _tail) const
{
std::cout << _x << std::endl;
return operator () (std::forward< T >(_tail)...);
}
template< typename... T >
result_type operator () (Z const &, T &&... _tail) const
{
std::cout << "z" << std::endl;
return operator () (std::forward< T >(_tail)...);
}
private :
result_type operator () () const { return; }
template< typename T, typename U >
using is_the_same = std::is_same< typename std::remove_const< typename std::remove_reference< T >::type >::type, U >;
template< typename T >
typename std::enable_if< is_the_same< T, std::string >::value, std::string >::type
forward(T && _s) const
{
return "\"" + _s + "\"";
}
template< typename T >
typename std::enable_if< is_the_same< T, double >::value, double >::type
forward(T && _x) const
{
return _x + 1.0;
}
};
int main()
{
test test_;
double x = 0.0;
std::string s = "s";
Z z;
test_.apply_filter(x, s, z);
return EXIT_SUCCESS;
}
But std::forward has a higher priority than member function in apply_filter. Thus there is no filtering occurs here.
Is there any workaround?
This should do the trick:
#include <iostream>
#include <utility>
struct Z { };
struct test
{
using result_type = void;
template< typename... P >
result_type apply_filter(P &&... _p) const
{
return operator () (forward<P>(_p)...);
}
template< typename... T >
result_type operator () (std::string const & _s, T &&... _tail) const
{
std::cout << _s << std::endl;
return operator () (std::forward< T >(_tail)...);
}
template< typename... T >
result_type operator () (double const & _x, T &&... _tail) const
{
std::cout << _x << std::endl;
return operator () (std::forward< T >(_tail)...);
}
template< typename... T >
result_type operator () (Z const &, T &&... _tail) const
{
std::cout << "z" << std::endl;
return operator () (std::forward< T >(_tail)...);
}
private:
result_type operator () () const { }
template< typename T, typename U >
using is_the_same = std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type, U>;
template<typename T>
using is_known_type = typename std::conditional<(is_the_same<T, std::string>::value || is_the_same<T, double>::value), std::true_type, std::false_type>::type;
template<typename T>
typename std::enable_if<is_the_same<T, std::string>::value, std::string>::type
forward_impl(T && _s, std::true_type) const
{
return "\"" + _s + "\"";
}
template<typename T>
typename std::enable_if<is_the_same<T, double>::value, double>::type
forward_impl(T && _x, std::true_type) const
{
return _x + 1.0;
}
#define RETURNS(exp) -> decltype(exp) { return exp; }
template<typename T>
auto forward_impl(T && t, std::false_type) const
RETURNS(std::forward<T>(t))
template<typename T>
auto forward(T && t) const
RETURNS(forward_impl(std::forward<T>(t), is_known_type<T>()))
#undef RETURNS
};
int main(int argc, char ** argv) {
test test_;
double x = 0.0;
std::string s = "s";
Z z;
test_.apply_filter(x, s, z);
return 0;
}
I'm trying to implement a reduce function but I don't know how to take the return type of the lambda:
template <typename IT, typename F, typename OT = IT>
auto reducef(const IT& input, F func) -> decltype(func(IT::value_type))
{
decltype(func(typename IT::value_type)) result = {};
return std::accumulate(input.begin(), input.end(), result, func);
}
Compiler output is the following:
test.cpp: In function ‘int main(int, char**)’:
test.cpp:37:80: error: no matching function for call to ‘reducef(std::vector<int>&, main(int, char**)::<lambda(const int&, const int&)>)’
test.cpp:37:80: note: candidate is:
test.cpp:22:6: note: template<class IT, class F, class OT> decltype (func(IT:: value_type)) reducef(const IT&, F)
test.cpp:22:6: note: template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<class IT, class F, class OT> decltype (func(IT:: value_type)) reducef(const IT&, F) [with IT = std::vector<int>; F = main(int, char**)::<lambda(const int&, const int&)>; OT = std::vector<int>]’:
test.cpp:37:80: required from here
test.cpp:22:6: error: dependent-name ‘IT:: value_type’ is parsed as a non-type, but instantiation yields a type
test.cpp:22:6: note: say ‘typename IT:: value_type’ if a type is meant
Extended use case:
template <typename IT, typename F, typename OT = IT>
OT mapf(const IT& input, F func)
{
OT output;
output.resize(input.size());
std::transform(input.begin(), input.end(), output.begin(), func);
return output;
}
template <typename IT, typename F, typename OT = IT>
auto reducef(const IT& input, F func) -> decltype(func(IT::value_type))
{
typename IT::value_type result = {};
return std::accumulate(input.begin(), input.end(), result, func);
}
int main(int argc, char *argv[])
{
vector<int> v1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
auto v2 = mapf(v1, [](const int& i) { return i+1;});
for (const auto& i : v2)
{
cout << i << endl;
}
cout << reducef(v1, [](const int& i, const int& j) -> int { return i + j; }) << endl;
}
I think you want to declare you return type as something like
decltype(func(std::declval<typename IT::value_type>(),
std::declval<typename IT::value_type>()))
Here is a complete testcase:
#include <algorithm>
#include <utility>
#include <vector>
#include <numeric>
template <typename IT, typename F, typename OT = IT>
auto reducef(const IT& input, F func)
-> decltype(func(std::declval<typename IT::value_type>(),
std::declval<typename IT::value_type>()))
{
decltype(func(std::declval<typename IT::value_type>(),
std::declval<typename IT::value_type>())) result{};
return std::accumulate(input.begin(), input.end(), result, func);
}
int main()
{
std::vector<int> values;
reducef(values, [](int a, int b) { return a + b; });
}