How can a variadic template be used to generate a left-associative expression (aka left fold) in c++11?
I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.
Such a template could be used as follows:
fold<add>(100,10,5) expands to add(add(100, 10), 5)
The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.
But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:
fold_left<div>(100,10,5) --> div(div(100, 10), 5) --> div(10, 5) --> 2
fold_right<div>(100,10,5) --> div(100, div(10, 5) --> div(100, 2) --> 50
It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:
#include <iostream>
template <typename T> using binary_op = T(*)(const T& a, const T& b);
// The terminal (single-argument) cases of the variadic functions defined later.
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }
// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_right(const T& t, Rest... rest) {
return Operation(t, fold_right<T, Operation>(rest...));
}
// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(Rest... rest, const T& t) {
return Operation(fold_left<T, Operation>(rest...), t);
}
inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }
int main() {
std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
<< "n"
<< fold_left<int,div>(100,10,5) // Compiler error!
<< std::endl;
return 0;
}
How can variadic templates be used (in c++11) to correcty implement fold_left?
I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.
Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).
Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.
As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.
Thanks!
c++ c++11 templates variadic-templates fold
add a comment |
I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.
Such a template could be used as follows:
fold<add>(100,10,5) expands to add(add(100, 10), 5)
The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.
But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:
fold_left<div>(100,10,5) --> div(div(100, 10), 5) --> div(10, 5) --> 2
fold_right<div>(100,10,5) --> div(100, div(10, 5) --> div(100, 2) --> 50
It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:
#include <iostream>
template <typename T> using binary_op = T(*)(const T& a, const T& b);
// The terminal (single-argument) cases of the variadic functions defined later.
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }
// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_right(const T& t, Rest... rest) {
return Operation(t, fold_right<T, Operation>(rest...));
}
// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(Rest... rest, const T& t) {
return Operation(fold_left<T, Operation>(rest...), t);
}
inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }
int main() {
std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
<< "n"
<< fold_left<int,div>(100,10,5) // Compiler error!
<< std::endl;
return 0;
}
How can variadic templates be used (in c++11) to correcty implement fold_left?
I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.
Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).
Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.
As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.
Thanks!
c++ c++11 templates variadic-templates fold
add a comment |
I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.
Such a template could be used as follows:
fold<add>(100,10,5) expands to add(add(100, 10), 5)
The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.
But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:
fold_left<div>(100,10,5) --> div(div(100, 10), 5) --> div(10, 5) --> 2
fold_right<div>(100,10,5) --> div(100, div(10, 5) --> div(100, 2) --> 50
It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:
#include <iostream>
template <typename T> using binary_op = T(*)(const T& a, const T& b);
// The terminal (single-argument) cases of the variadic functions defined later.
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }
// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_right(const T& t, Rest... rest) {
return Operation(t, fold_right<T, Operation>(rest...));
}
// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(Rest... rest, const T& t) {
return Operation(fold_left<T, Operation>(rest...), t);
}
inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }
int main() {
std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
<< "n"
<< fold_left<int,div>(100,10,5) // Compiler error!
<< std::endl;
return 0;
}
How can variadic templates be used (in c++11) to correcty implement fold_left?
I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.
Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).
Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.
As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.
Thanks!
c++ c++11 templates variadic-templates fold
I would like to use a c++ template to aggregate (fold) multiple arguments using a binary operation.
Such a template could be used as follows:
fold<add>(100,10,5) expands to add(add(100, 10), 5)
The particular expansion shown above is the "left fold". The expansion add(100, add(10, 5)) is the "right fold". Assuming that the add function performs simple integer addition, both right and left folds produce the same result, 115.
But consider a function div that performs integer division (div(a,b)=a/b). In this case, associativity matters and the left and right folds produce different results:
fold_left<div>(100,10,5) --> div(div(100, 10), 5) --> div(10, 5) --> 2
fold_right<div>(100,10,5) --> div(100, div(10, 5) --> div(100, 2) --> 50
It is straightforward to use a variadic template to produce the right-associative version (fold_right), but I have not been able to figure out how to produce the left-associative version (fold_left). The attempted implementation of fold_left below results in a compiler error:
#include <iostream>
template <typename T> using binary_op = T(*)(const T& a, const T& b);
// The terminal (single-argument) cases of the variadic functions defined later.
template<typename T, binary_op<T> Operation> inline T fold_right(const T& t) { return t; }
template<typename T, binary_op<T> Operation> inline T fold_left(const T& t) { return t; }
// Combines arguments using right-associative operation
// i.e. fold_right<T,op>(A,B,C) --> op(A, op(B,C))
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_right(const T& t, Rest... rest) {
return Operation(t, fold_right<T, Operation>(rest...));
}
// Combines arguments using left-associative operation
// i.e. fold_left<T,op>(A,B,C) --> op(op(A,B), C)
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(Rest... rest, const T& t) {
return Operation(fold_left<T, Operation>(rest...), t);
}
inline int add(const int& a, const int& b) { return a+b; }
inline int div(const int& a, const int& b) { return a/b; }
int main() {
std::cout << fold_right<int,div>(100,10,5) // (100 / (10 / 5)) = 50
<< "n"
<< fold_left<int,div>(100,10,5) // Compiler error!
<< std::endl;
return 0;
}
How can variadic templates be used (in c++11) to correcty implement fold_left?
I think it essentially comes down to being able to "pop" the last argument off of a parameter pack, which I attempted to do in the left_fold template above, but as I said, this resulted in a compiler error.
Note: I have used simple arithmetic operations and integers as an example in this question, but the answer(s) should be generic enough to handle aggregation of objects using an arbitrary function (assuming it returns the same type of object as its arguments).
Note 2: For those familiar with c++17, fold expressions can be used to produce both left and right folds with binary operators. But these are not available in c++11.
As a related question: The above templates require the type T to be explicitly specified, as in fold_right<int,div>(...). Is there some way to formulate the template so that only the operation is required, e.g. fold_right<div>(...). I would think the type T could be inferred, but I don't see a way to order the template arguments to put the binary_op<> first.
Thanks!
c++ c++11 templates variadic-templates fold
c++ c++11 templates variadic-templates fold
asked 15 hours ago
ricovoxricovox
2,0481828
2,0481828
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:
template<typename T, binary_op<T> Operation>
inline T fold_left(const T& t) { return t; }
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
return fold_left<T, Operation>(Operation(a,b), rest...);
}
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
add a comment |
Micheal answered your 1st Q.
The 2nd may have different answers. My prefered way is to difine your operations as functors with template members:
#include <type_traits>
struct homogene_add{
template<typename T>
T operator()(T const& lhs, T const& rhs){/*...*/}
};
struct mixed_add{
template<typename L, typename R>
std::common_type<L,R>::type
operator()(L const& lhs, R const& rhs){/*...*/}
};
template<typename binary_op, typename ... Args>
std::common_type<Args...>::type
fold_right(const Args&... args);
template<typename binary_op, typename First, typename ... Args>
std::common_type<First, Args...>::type
fold_right(const First& init, const Args&... args) {
binary_op op;
return op(init, fold_right<binary_op>(args...));
};
template<typename binary_op, typename First>
const First& fold_right(const First& init) {
return init;
};
CV qualification and valueness correctness, I leave to the OP.
Thanks for the suggestion. Unfortunatelyautocannot be used as a return type in c++11 without using trailing return type syntax (e.g.auto f() -> decltype(...)). Would it be possible to change your definitions offold_rightto be compatible with c++11 ?
– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration ofauto fold_right(const Args&... args);seems to cause a compiler error:error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’
– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is thatstd::common_typemight be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices.scalar*Matrixmultiplication is well-defined and produces a Matrix. AlsoMatrix*Matrixis well defined and produces another Matrix, so the expressionMatrix M3 = s1*M1*M2;(wheres1is a scalar andM's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and causefold_rightto fail).
– ricovox
2 hours ago
|
show 1 more comment
That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.
That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54760418%2fhow-can-a-variadic-template-be-used-to-generate-a-left-associative-expression-a%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:
template<typename T, binary_op<T> Operation>
inline T fold_left(const T& t) { return t; }
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
return fold_left<T, Operation>(Operation(a,b), rest...);
}
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
add a comment |
Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:
template<typename T, binary_op<T> Operation>
inline T fold_left(const T& t) { return t; }
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
return fold_left<T, Operation>(Operation(a,b), rest...);
}
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
add a comment |
Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:
template<typename T, binary_op<T> Operation>
inline T fold_left(const T& t) { return t; }
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
return fold_left<T, Operation>(Operation(a,b), rest...);
}
Parameter packs on the left are problematic. Better reimplement it as a parameter pack on the right:
template<typename T, binary_op<T> Operation>
inline T fold_left(const T& t) { return t; }
template<typename T, binary_op<T> Operation, typename ... Rest>
inline T fold_left(const T& a, const T& b, Rest... rest) {
return fold_left<T, Operation>(Operation(a,b), rest...);
}
answered 15 hours ago
Michael VekslerMichael Veksler
3,0671516
3,0671516
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
add a comment |
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
1
1
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
It compiles and seems to work. Live Demo on coliru
– Scheff
15 hours ago
add a comment |
Micheal answered your 1st Q.
The 2nd may have different answers. My prefered way is to difine your operations as functors with template members:
#include <type_traits>
struct homogene_add{
template<typename T>
T operator()(T const& lhs, T const& rhs){/*...*/}
};
struct mixed_add{
template<typename L, typename R>
std::common_type<L,R>::type
operator()(L const& lhs, R const& rhs){/*...*/}
};
template<typename binary_op, typename ... Args>
std::common_type<Args...>::type
fold_right(const Args&... args);
template<typename binary_op, typename First, typename ... Args>
std::common_type<First, Args...>::type
fold_right(const First& init, const Args&... args) {
binary_op op;
return op(init, fold_right<binary_op>(args...));
};
template<typename binary_op, typename First>
const First& fold_right(const First& init) {
return init;
};
CV qualification and valueness correctness, I leave to the OP.
Thanks for the suggestion. Unfortunatelyautocannot be used as a return type in c++11 without using trailing return type syntax (e.g.auto f() -> decltype(...)). Would it be possible to change your definitions offold_rightto be compatible with c++11 ?
– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration ofauto fold_right(const Args&... args);seems to cause a compiler error:error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’
– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is thatstd::common_typemight be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices.scalar*Matrixmultiplication is well-defined and produces a Matrix. AlsoMatrix*Matrixis well defined and produces another Matrix, so the expressionMatrix M3 = s1*M1*M2;(wheres1is a scalar andM's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and causefold_rightto fail).
– ricovox
2 hours ago
|
show 1 more comment
Micheal answered your 1st Q.
The 2nd may have different answers. My prefered way is to difine your operations as functors with template members:
#include <type_traits>
struct homogene_add{
template<typename T>
T operator()(T const& lhs, T const& rhs){/*...*/}
};
struct mixed_add{
template<typename L, typename R>
std::common_type<L,R>::type
operator()(L const& lhs, R const& rhs){/*...*/}
};
template<typename binary_op, typename ... Args>
std::common_type<Args...>::type
fold_right(const Args&... args);
template<typename binary_op, typename First, typename ... Args>
std::common_type<First, Args...>::type
fold_right(const First& init, const Args&... args) {
binary_op op;
return op(init, fold_right<binary_op>(args...));
};
template<typename binary_op, typename First>
const First& fold_right(const First& init) {
return init;
};
CV qualification and valueness correctness, I leave to the OP.
Thanks for the suggestion. Unfortunatelyautocannot be used as a return type in c++11 without using trailing return type syntax (e.g.auto f() -> decltype(...)). Would it be possible to change your definitions offold_rightto be compatible with c++11 ?
– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration ofauto fold_right(const Args&... args);seems to cause a compiler error:error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’
– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is thatstd::common_typemight be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices.scalar*Matrixmultiplication is well-defined and produces a Matrix. AlsoMatrix*Matrixis well defined and produces another Matrix, so the expressionMatrix M3 = s1*M1*M2;(wheres1is a scalar andM's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and causefold_rightto fail).
– ricovox
2 hours ago
|
show 1 more comment
Micheal answered your 1st Q.
The 2nd may have different answers. My prefered way is to difine your operations as functors with template members:
#include <type_traits>
struct homogene_add{
template<typename T>
T operator()(T const& lhs, T const& rhs){/*...*/}
};
struct mixed_add{
template<typename L, typename R>
std::common_type<L,R>::type
operator()(L const& lhs, R const& rhs){/*...*/}
};
template<typename binary_op, typename ... Args>
std::common_type<Args...>::type
fold_right(const Args&... args);
template<typename binary_op, typename First, typename ... Args>
std::common_type<First, Args...>::type
fold_right(const First& init, const Args&... args) {
binary_op op;
return op(init, fold_right<binary_op>(args...));
};
template<typename binary_op, typename First>
const First& fold_right(const First& init) {
return init;
};
CV qualification and valueness correctness, I leave to the OP.
Micheal answered your 1st Q.
The 2nd may have different answers. My prefered way is to difine your operations as functors with template members:
#include <type_traits>
struct homogene_add{
template<typename T>
T operator()(T const& lhs, T const& rhs){/*...*/}
};
struct mixed_add{
template<typename L, typename R>
std::common_type<L,R>::type
operator()(L const& lhs, R const& rhs){/*...*/}
};
template<typename binary_op, typename ... Args>
std::common_type<Args...>::type
fold_right(const Args&... args);
template<typename binary_op, typename First, typename ... Args>
std::common_type<First, Args...>::type
fold_right(const First& init, const Args&... args) {
binary_op op;
return op(init, fold_right<binary_op>(args...));
};
template<typename binary_op, typename First>
const First& fold_right(const First& init) {
return init;
};
CV qualification and valueness correctness, I leave to the OP.
edited 3 hours ago
answered 14 hours ago
Red.WaveRed.Wave
77537
77537
Thanks for the suggestion. Unfortunatelyautocannot be used as a return type in c++11 without using trailing return type syntax (e.g.auto f() -> decltype(...)). Would it be possible to change your definitions offold_rightto be compatible with c++11 ?
– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration ofauto fold_right(const Args&... args);seems to cause a compiler error:error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’
– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is thatstd::common_typemight be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices.scalar*Matrixmultiplication is well-defined and produces a Matrix. AlsoMatrix*Matrixis well defined and produces another Matrix, so the expressionMatrix M3 = s1*M1*M2;(wheres1is a scalar andM's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and causefold_rightto fail).
– ricovox
2 hours ago
|
show 1 more comment
Thanks for the suggestion. Unfortunatelyautocannot be used as a return type in c++11 without using trailing return type syntax (e.g.auto f() -> decltype(...)). Would it be possible to change your definitions offold_rightto be compatible with c++11 ?
– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration ofauto fold_right(const Args&... args);seems to cause a compiler error:error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’
– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is thatstd::common_typemight be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices.scalar*Matrixmultiplication is well-defined and produces a Matrix. AlsoMatrix*Matrixis well defined and produces another Matrix, so the expressionMatrix M3 = s1*M1*M2;(wheres1is a scalar andM's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and causefold_rightto fail).
– ricovox
2 hours ago
Thanks for the suggestion. Unfortunately
auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?– ricovox
4 hours ago
Thanks for the suggestion. Unfortunately
auto cannot be used as a return type in c++11 without using trailing return type syntax (e.g. auto f() -> decltype(...)). Would it be possible to change your definitions of fold_right to be compatible with c++11 ?– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration of
auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’– ricovox
4 hours ago
I'd like to use this approach, but even when compiling with c++14, your forward declaration of
auto fold_right(const Args&... args); seems to cause a compiler error: error: use of ‘auto fold_right(const Args& ...) [with binary_op= homogene_add; Args = {int, int, int, int}]’ before deduction of ‘auto’– ricovox
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox I agree. This is not a complete answer. You can work on the correct return type instead of that while I prepare for an edit.
– Red.Wave
4 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
@ricovox that one is the template delaration; It can return void, as long as specializations are correctly defined.
– Red.Wave
3 hours ago
The problem I see with your updated version is that
std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).– ricovox
2 hours ago
The problem I see with your updated version is that
std::common_type might be the wrong return type. For example, suppose we want to multiply a list of scalars and Matrices. scalar*Matrix multiplication is well-defined and produces a Matrix. Also Matrix*Matrix is well defined and produces another Matrix, so the expression Matrix M3 = s1*M1*M2; (where s1 is a scalar and M's are matrices) would compile just fine. But a scalar cannot be implicitly converted into a Matrix (and vice-versa) so std::common_type<scalar,Matrix> would fail (and cause fold_right to fail).– ricovox
2 hours ago
|
show 1 more comment
That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.
That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
add a comment |
That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.
That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
add a comment |
That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.
That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.
That is the magic of non-trailing function parameter packs: they are only deduced from the explicitly provided template parameters.
That means rest... is empty in the fold_left<int,div>(100,10,5) call. Therefore, your function has a single argument, not 3.
answered 15 hours ago
AcornAcorn
5,66111238
5,66111238
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
add a comment |
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
1
1
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
Thanks, that explains the compiler error ("template argument deduction/substitution failed: candidate expects 1 argument, 3 provided"). But how can I rewrite fold_left to do what I want?
– ricovox
15 hours ago
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54760418%2fhow-can-a-variadic-template-be-used-to-generate-a-left-associative-expression-a%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown