How can a variadic template be used to generate a left-associative expression (aka left fold) in c++11?












9















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!










share|improve this question



























    9















    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!










    share|improve this question

























      9












      9








      9








      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!










      share|improve this question














      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 15 hours ago









      ricovoxricovox

      2,0481828




      2,0481828
























          3 Answers
          3






          active

          oldest

          votes


















          9














          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...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            15 hours ago





















          2














          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.






          share|improve this answer


























          • 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













          • @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 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





















          0














          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.






          share|improve this answer



















          • 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













          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          9














          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...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            15 hours ago


















          9














          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...);
          }





          share|improve this answer



















          • 1





            It compiles and seems to work. Live Demo on coliru

            – Scheff
            15 hours ago
















          9












          9








          9







          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...);
          }





          share|improve this answer













          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...);
          }






          share|improve this answer












          share|improve this answer



          share|improve this answer










          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
















          • 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















          2














          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.






          share|improve this answer


























          • 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













          • @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 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


















          2














          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.






          share|improve this answer


























          • 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













          • @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 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
















          2












          2








          2







          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 3 hours ago

























          answered 14 hours ago









          Red.WaveRed.Wave

          77537




          77537













          • 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













          • @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 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





















          • 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













          • @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 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



















          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













          0














          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.






          share|improve this answer



















          • 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


















          0














          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.






          share|improve this answer



















          • 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
















          0












          0








          0







          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.






          share|improve this answer













          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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          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
















          • 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




















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Callistus I

          Tabula Rosettana

          How to label and detect the document text images