abc
abc

Reputation: 1004

c++ Default paramaters: is it possible to override a default parameter without overriding earlier default parameters

I have a function:

int function(int a, int b = 1, int c = 2){
    return a+b+c;
}

I want to set the value of the "c" variable to 3, but don't want to set the value of "b"

In a language like python I can do this:

function(23,c=3)

However in c++ I cant find a way to do something like that. All examples I could find involved setting the value of "b" before the value of "c", like this:

function(23,1,3);

How can I set the value of a default parameter directly?

Upvotes: 2

Views: 2928

Answers (5)

Brian Vandenberg
Brian Vandenberg

Reputation: 4121

Something like this could be done with the named parameter idiom. Here's how it might look in use to have optional parameters (sans the default parameter values):

/*
int function(int a, int b = 1, int c = 2){
  return a+b+c;
}
*/

int function( Parameters &p ) {
  /* ... */
}

void someOtherFunction() {
  function( Parameters().parmW(/*...*/)
                        /* parmX was omitted here */
                        .parmY(/*...*/)
                        .parmZ(/*...*/)
          );

Adding default parameters could be done in a few ways. function could be replaced with a class whose purpose is to perform those actions. Parameters could also be written to know which flags were set, then function passes in default values before it begins executing. I'm sure there's plenty of ways to do this, perhaps some a lot better than what I've suggested.

Upvotes: 0

Richard Hodges
Richard Hodges

Reputation: 69864

It is possible in c++... if you're willing to jump through some hoops.

For fun, here is an example of how it might be done:

#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>

//
// utility to check whether a type is in a list of types
//
template<class T, class...Ts> struct is_in;

template<class T, class U>
struct is_in<T, U>
: std::is_same<T, U>::type {};

template<class T, class U, class...Rest>
struct is_in<T, U, Rest...>
: std::integral_constant<bool, std::is_same<T, U>::value || is_in<T, Rest...>::value>
{};

//
// a wrapper around fundamental types so we can 'name' types
//

template<class Type, class Tag>
struct fundamental {
    using value_type = Type;
    using tag_type = Tag;

    fundamental(Type x) : _x(x) {}

    operator const Type&() const { return _x; }
    operator Type&() { return _x; }

    Type _x;
};

//
// a utility to figure out a fundamental type's value or to take it's default value if it's not present
//
template<class Fundamental, class Tuple, typename = void>
struct value_of_impl
{
    static typename Fundamental::value_type apply(const Tuple& t)
    {
        return Fundamental::tag_type::dflt;
    }
};

template<class Fundamental, class...Types>
struct value_of_impl<Fundamental, std::tuple<Types...>, std::enable_if_t<is_in<Fundamental, Types...>::value>>
{
    static typename Fundamental::value_type apply(const std::tuple<Types...>& t)
    {
        return typename Fundamental::value_type(std::get<Fundamental>(t));
    }
};

template<class Fundamental, class Tuple>
decltype(auto) value_of(const Tuple& t)
{
    return value_of_impl<Fundamental, Tuple>::apply(t);
}


//
// some tag names to differentiate parameter 'name' types
//
struct a_tag { static constexpr int dflt = 0; };
struct b_tag { static constexpr int dflt = 1; };
struct c_tag { static constexpr int dflt = 2; };

//
// define some parameter 'names'
//
using a = fundamental<int, a_tag>;
using b = fundamental<int, b_tag>;
using c = fundamental<int, c_tag>;

//
// the canonical implementation of the function
//
void func(int a, int b, int c)
{
    std::cout << a << ", " << b << ", " << c << std::endl;
}

//
// a version that forwards the values of fundamental types in a tuple, or their default values if not present
//
template<class...Fundamentals>
void func(std::tuple<Fundamentals...> t)
{
    func(value_of<a>(t),
         value_of<b>(t),
         value_of<c>(t));
}

//
// a version that converts a variadic argument list of fundamentals into a tuple (that we can search)
//
template<class...Fundamentals>
void func(Fundamentals&&...fs)
{
    return func(std::make_tuple(fs...));
}

//
// a test
//

using namespace std;

auto main() -> int
{
    func();
    func(a(5));
    func(c(10), a(5));
    func(b(20), c(10), a(5));

    return 0;
}

expected output:

0, 1, 2
5, 1, 2
5, 1, 10
5, 20, 10

Upvotes: 2

Anton Savin
Anton Savin

Reputation: 41301

You can't do that directly, but you can use Named Parameter Idiom (although criticized).

The idea is to create an object encapsulating all parameters, initialize it using method chaining and finally call the function, so the code would look like:

int v = function(params(23).c(3));

Upvotes: 1

mziccard
mziccard

Reputation: 2178

You can not do that in C++.

As a workaround you could wrap all parameters as fields with default value in a class (or a struct). You can then have multiple constructors for that class that allow you to set only those fields you are really interested in changing with respect to default.

Upvotes: 3

Baum mit Augen
Baum mit Augen

Reputation: 50053

This is not possible in C++ (at least not directly). You have the provide all parameters up to the last one you want to provide, and in the order given by the declaration.

Upvotes: 5

Related Questions