3

There exist many similar questions for C, e.g. this one, but i'm searching for the simplest and most readable way in C++ to pass an C-array into a function without first defining a variable for it.

Given a void f(double const* a) these are not possible (but the first one is in C99):

f((double []){1,2}); // works in C
f({1,2}); // would be great but doesn't work in any language with C-arrays

Of course defining a variable first is always possible, but that's not what i'm looking for:

double a[] = { 1.2, 3.4 };
f(a);

However, it is possible to wrap things into a struct, f.e. like

struct A { double a[2]; };
f(A{2.3,4.5}.a);

and that type can be reused, so that's already a little win

Live Demo

but this still feels clumsy.

Is there a simpler and more readable way? In one of the current standards or maybe an upcoming one?

EDIT: Sorry, i didn't mention that before; i can't change the target api. A pointer to a double is expected.

ridilculous
  • 624
  • 3
  • 16
  • 3
    "so that's already a little win". Win for who? Certainly not for the next person reading your code. – gsamaras Apr 24 '23 at 12:30
  • In the first example, `double *[]` is a pointer to a pointer to `double`, not a poster to `double`. No way that's going to match. – Pete Becker Apr 24 '23 at 12:30
  • @churill true but i can't change the target api in this case – ridilculous Apr 24 '23 at 12:42
  • @PeteBecker If with the first example you're referring to `f((double *[]){1,2});` than that's the code from the question about the C-version i linked. I didn't change that code but added it for completeness sake. Is it more intuitive for the question if i remove it or add a title that it's from the C question? – ridilculous Apr 24 '23 at 12:47
  • 1
    I believe you confused things. Your linked C question intends to address an array of strings i.e. an array of arrays of `char`s. Hence, the `char *[]` makes sense. If you try to address an array of `double`s, `double *[]` is plain wrong - in C as well as C++. Furthermore, I don't think referring to C is of much value here. C and C++ are different languages, and that something is correct in one of them doesn't necessarily mean anything for the other. – Scheff's Cat Apr 24 '23 at 13:17
  • @Scheff'sCat You're right. Thanks for the clarification! It should be `f((double []){1.,2.});` --> [Live Demo](https://godbolt.org/z/hW18asY4n) – ridilculous Apr 24 '23 at 14:19

4 Answers4

6

This function is great for that:

template<typename T, std::size_t N>
constexpr T* temporary_array(T(&& arr)[N]) noexcept { return arr; }

Then you can simply call f(temporary_array<double>({1,2})); (or f(temporary_array({1., 2.}))).

In C++20, you can write f(std::type_identity_t<double(&&)[]>{1,2}). f(std::type_identity_t<double[]>{1, 2}) should also work in any C++ version, but GCC has a long-standing bug that doesn't allow this to compile.

Looking for standard library solutions, your struct A can be replaced with std::array<double, 2>. Writing out the size parameter is annoying, but it can be replaced with CTAD:

f(std::array{ 1., 2. }.data());
f(std::array<double, 2>{1, 2}.data());
// (These will call move constructors)
f(std::to_array<double>({1, 2}).data());
f(std::to_array({1., 2.}).data());
Artyer
  • 31,034
  • 3
  • 47
  • 75
3

How about you use a vector instead of a double*? Then you can construct one in-place even at call site, like so:

#include<vector>

double f(std::vector<double> const& v)
{
  return v[0];
}


int main()
{
  f({1,2,3});
}
Michael Kopp
  • 1,571
  • 12
  • 17
1

If you can compile with c++20, the obvious answer is to use std::span, for maximum flexibility.

Demo:

#include <array>
#include <span>
#include <vector>

void f(std::span<const double> a);

int main()
{
    f({{1.0, 2.0, 3.3}});

    double ca[] = { 1.2, 3.4 };
    f(ca);

    std::vector<double> v = { 1.2, 3.4 };
    f(v);

    std::array a = { 1.2, 3.4 };
    f(a);
}
cptFracassa
  • 893
  • 4
  • 14
1

If you want to avoid dynamic allocation, use std::initializer_list

template <typename T>
void f(const std::initializer_list<T>& v)
{
    for (const auto& e : v)
        std::cout << e << " ";
    std::cout << "\n";
}


int main()
{
    f({1,2,3});
    f({4.0,5.1,6.2});
}

outputs

1 2 3 
4 5.1 6.2 

Live example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402