Function and its parameters

Hello everyone. I remember an article explaining how to manage functions and their parameters. However, I remember an explanation about some additional parameters with (as a parameter) three dots. I read so many papers or books, and now I cannot say that it was for C# or C++ - or something else. A function could be written this way :

myFunction(int firstParameter, int secondParameter, int thirdParameter, ...){ // do something }

I don't talk about functions overloading. I cannot find anything about these three dots. Do you know something about them? Thank you for your help. Have a nice day ++
Last edited on
Exactly what I was searching. Well done. Thank you George ++
Note carefully the situation changed somewhat with C++11. It added template parameter packs.
https://en.cppreference.com/w/cpp/language/parameter_pack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <cstdarg>

float average(int num,...) 
{
   va_list valist;
   float sum = 0.0;
   // initialize va_list - num for number of arguments
   va_start(valist, num);
   // for each argument
   for (int i = 0; i < num; i++)
      sum += va_arg(valist, int);
   // compute average
   float result = sum/num;
   // clean up
   va_end(valist);
   return result;
}

int main() 
{
   std::cout << "Average (1, 2, 3, 4) = "<< average(4,1,2,3,4) << std::endl;
   std::cout << "Average (10, 20, 30) = "<< average(3,10,20,30)<< std::endl;
}


Thank you for the links. However I have to set a first parameter according to my number of arguments. In other case, I get an error in my computation. Is there a way to create a function without any "formal" parameter ?
Last edited on
*AHEM!*

C++11's "parameter pack." (See my 2nd post with link)

C++17 made some nice modifications to the usage.
https://riptutorial.com/cplusplus/example/3208/iterating-over-a-parameter-pack

C++20 made some additional modifications, can't seem to find an example. But then I really haven't bothered to look.

You should make the effort since this is something you want to know.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

double avsum( double a )
{
   return a;
}

template <typename... Args> double avsum( double a, Args... b )
{
   double total = a + avsum( b... );
   return total;
}

template <typename... Args> double average( Args... a )
{
   return avsum( a... ) / sizeof...( a );
}


int main()
{
   cout << average( 1, 2, 3, 4 ) << '\n';
}



Since C++17 you can do without the base case (but this wouldn't run in cpp.sh).
1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
using namespace std;

template <typename... Args> double average( Args... a )
{
   return ( 0.0 + ... + a ) / sizeof...( a );              // C++17
}

int main()
{
   cout << average( 1, 2, 3, 4 ) << '\n';
}




To be honest, though, it's simpler, and the compiler will create much less code, without variadic arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <valarray>
using namespace std;

double average( const valarray<double> &V )
{
   return V.sum() / V.size();
}

int main()
{
   cout << average( { 1, 2, 3, 4 } ) << '\n';
}

Last edited on
The first param does not need to be the number of args. But there needs to be some form of 'end of list' detection - which could be a sentinel value (say -1 in this case) etc. If va_arg is called when no valid argument exists, then this is an error in some way (undefined behaviour). Incidentally, that's one of the issues with printf() which uses the ... argument format.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <cstdarg>

double average(int num, ...) {
	va_list valist;
	int sum {num};
	size_t cnt {1};

	va_start(valist, num);
	for (int val {}; (val = va_arg(valist, int)) != -1; sum += val, ++cnt);
	va_end(valist);
	return (sum + 0.0) / cnt;
}

int main() {
	std::cout << "Average (1, 2, 3, 4) = " << average(1, 2, 3, 4, -1) << '\n';
	std::cout << "Average (10, 20, 30) = " << average(10, 20, 30, -1) << '\n';
}


Or using fold expressions from C++17 (assuming at least 1 value):

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

template <typename ...Args>
auto average(const Args& ...value) {
	return (0.0 + ... + value) / sizeof ...(value);
}

int main() {
	std::cout << "Average (1, 2, 3, 4) = " << average(1, 2, 3, 4) << '\n';
	std::cout << "Average (10, 20, 30) = " << average(10, 20, 30) << '\n';
}

Last edited on
I am really sorry if I bothered you George. It's not always easy for me to understand some explanations in English. I took a look at your previous links. However I lost many time, trying to adapt another code which seems to me interesting - but not really efficient :

https://stackoverflow.com/questions/4421681/how-to-count-the-number-of-arguments-passed-to-a-function-that-accepts-a-variabl

@lastchance, your last example is better than my attempt using cstdarg. Neat. I did not know that I could use an array so as to check arguments. @seeplus, thank you for the tips. It is really clever to use -1 in order to check the end of the list. An interesting alternative.

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

template <typename ...Args>
auto average(const Args& ...value) {
	return (0.0 + ... + value) / sizeof ...(value);
}

int main() 
{
    std::cout << "Average (1, 2, 3, 4) = " << average(1, 2, 3, 4) << '\n';
    std::cout << "Average (10, 20, 30) = " << average(10, 20, 30) << '\n';
}


This one does not work for me with the latest VS version, but it works as expected on Compiler Explorer. However, I prefer the last one with valarray which supports element-wise mathematical operations and various forms of generalized subscript operators, slicing and indirect access (as they say). Thank you everyone ++

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <valarray>
using namespace std;

double average( const valarray<double> &V )
{
   return V.sum() / V.size();
}

int main()
{
   cout << average( { 1, 2, 3, 4 } ) << '\n';
}
Last edited on
One of the biggest reasons why I tend to avoid using variadic arguments/parameter packs as function parameters is they don't do type checking. Something key to C++.

lastchance shows an example that preserves type checking, using a C++ feature that is usually ignored. I know std::valarray<> wouldn't be something I'd use without some thinking.

This one does not work for me with the latest VS version

That example requires C++17 (or later), VS defaults to compiling using C++14.

You should consider modifying your defaults globally to use /std:c++20 (/std:c++latest actually if you want to use C++20 features) so each new project/solution will automatically use /std:c++20 or /std::c++latest.

See helios' reply for how to make global modifications to the default project properties:
http://www.cplusplus.com/forum/lounge/271176/#msg1169093

And then my follow-up reply how to find the Property Manager window.

Now, instead of adding library directories globally you want to modify the C++ Language Standard used in Common Properties -> C/C++ -> Language.

Change the language standard for Win32 and x64, it doesn't matter if Debug or Release. I choose Release.

Close the Property Manager window, you may get a dialogue to save changed properties. YES! Save them.

Now, if this change happened every new project you create should use the later language standard you selected.

I really recommend /std:c++latest. That is what I chose for my defaults.

[Edited to Add]: And, yes, this works for VS 2022 as well as VS 2017/2019. Every new Boost release I modify my default library paths to the new Boost version. Takes just a couple of minutes.

Downloading and building Boost takes some time. I have that build procedure down to a couple of console commands. Build for both Win32 and x64, Debug and Release.
Last edited on
Topic archived. No new replies allowed.