Как передать литерал константного массива функции, которая принимает указатель без использования переменной C/С++?
Если у меня есть прототип, который выглядит так:
function(float,float,float,float)
Я могу передать такие значения:
function(1,2,3,4);
Итак, если мой прототип:
function(float*);
Есть ли способ добиться чего-то подобного?
function( {1,2,3,4} );
Просто ищет ленивый способ сделать это, не создавая временную переменную, но я не могу прикрыть синтаксис.
Ответы
Ответ 1
Вы можете сделать это на C99 (но не на ANSI C (C90) или любом текущем варианте С++) с составными литералами. См. Раздел 6.5.2.5 стандарта C99 для деталей gory. Вот пример:
// f is a static array of at least 4 floats
void foo(float f[static 4])
{
...
}
int main(void)
{
foo((float[4]){1.0f, 2.0f, 3.0f, 4.0f}); // OK
foo((float[5]){1.0f, 2.0f, 3.0f, 4.0f, 5.0f}); // also OK, fifth element is ignored
foo((float[3]){1.0f, 2.0f, 3.0f}); // error, although the GCC doesn't complain
return 0;
}
GCC также предоставляет это как расширение для C90. Если вы скомпилируете -std=gnu90
(по умолчанию), -std=c99
или -std=gnu99
, он скомпилирует; если вы скомпилируете -std=c90
, это не будет.
Ответ 2
Это отмечено как C, так и С++, поэтому вы получите радикально разные ответы.
Если вы ожидаете четыре параметра, вы можете сделать это:
void foo(float f[])
{
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
float f[] = {1, 2, 3, 4};
foo(f);
}
Но это довольно опасно, поскольку вы можете сделать это случайно:
void foo(float f[])
{
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
float f[] = {1, 2}; // uh-oh
foo(f);
}
Обычно лучше оставить их в качестве отдельных параметров. Поскольку вы все равно не должны использовать необработанные массивы, вы можете сделать это:
#include <cassert>
#include <vector>
void foo(std::vector<float> f)
{
assert(f.size() == 4);
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
float f[] = {1, 2, 3, 4};
foo(std::vector<float>(f, f + 4)); // be explicit about size
// assert says you cannot do this:
foo(std::vector<float>(f, f + 2));
}
Улучшение, но не много. Вы можете использовать boost::array
, но вместо ошибки для несоответствующего размера они инициализируются на 0:
#include <boost/array.hpp>
void foo(boost::array<float, 4> f)
{
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
boost::array<float, 4> f = {1, 2, 3, 4};
foo(f);
boost::array<float, 4> f2 = {1, 2}; // same as = {1, 2, 0, 0}
foo(f2);
}
Все это будет исправлено в С++ 0x, когда добавляются конструкторы списка инициализаторов:
#include <cassert>
#include <vector>
void foo(std::vector<float> f)
{
assert(f.size() == 4);
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
foo({1, 2, 3, 4}); // yay, construct vector from this
// assert says you cannot do this:
foo({1, 2});
}
И, вероятно, boost::array
:
#include <boost/array.hpp>
void foo(boost::array<float, 4> f)
{
float f0 = f[0];
float f1 = f[1];
float f2 = f[2];
float f3 = f[3];
}
int main(void)
{
foo({1, 2, 3, 4});
foo({1, 2}); // same as = {1, 2, 0, 0} ..? I'm not sure,
// I don't know if they will do the check, if possible.
}
Ответ 3
Вы можете создать составной литерал:
function ((float[2]){2.0, 4.0});
Хотя, я не уверен, почему вы хотите решить эту проблему. Это не разрешено ISO.
Как правило, таких ярлыков следует избегать в целях удобства чтения во всех случаях; лень не является хорошей привычкой исследовать (личное мнение, конечно)
Ответ 4
Плохая новость заключается в том, что для этого нет синтаксиса. Хорошей новостью является то, что это изменится со следующей официальной версией стандарта С++ (ожидается в следующем году или двух). Новый синтаксис будет выглядеть так, как вы описываете.
Ответ 5
Вы можете технически ссылаться на массив, но вы все равно не можете создать анонимный список инициализаторов, я думаю.
void func(int (&bla)[4])
{
int count = sizeof(bla)/sizeof(bla[0]);
// count == 4
}
int bla[] = {1, 2, 3, 4};
func(bla);
int bla1[] = {1, 2};
func(bla1); // <-- fails
Для С++, посмотрите boost:: assign. Довольно аккуратный способ заполнения контейнеров STL.
Ответ 6
Нет, вы не можете этого сделать. У меня нет стандарта, доступного здесь, поэтому я не могу дать точную ссылку, но самое близкое к тому, что вы просите, это строковые константы, т.е.
function(char *);
function("mystring");
обрабатывается компилятором как
char * some_pointer = "mystring";
function(char *);
function(some_pointer);
Невозможно обработать другие типы переменных таким образом.
Ответ 7
К сожалению, он работает только с массивами символов:
void func2(char arg[]) {
}
int main()
{
func2("hello");
return 0;
}
Ответ 8
вы можете написать класс строителя, который позволит примерно такой же синтаксис
// roughly
template <typename C>
class Builder {
public:
template <typename T>
Builder(const T & _data) { C.push_back(_data); }
template <typename T>
Builder& operator()(const T & _data) {
C.push_back(_data);
return *this;
}
operator const C & () const { return data; }
private:
C data;
};
таким образом, вы можете использовать класс как
foo (const std::vector и v);
foo (Builder < std::vector > (1) (2) (3) (4));
Ответ 9
Чтобы добавить к забаве, вы можете использовать шаблоны, чтобы сделать ее переменной по длине.
template<std::size_t N>
int chars(const char(&r)[N]){
std::cout << N << ": " << r << std::endl;
return 0;
}
template<std::size_t N>
int floats(const float(&r)[N]){
std::cout << N << ":";
for(size_t i = 0; i < N; i++)
std::cout << " " << r[i];
std::cout << std::endl;
return 0;
}
int main(int argc, char ** argv) {
chars("test");
floats({1.0f, 2.0f, 3.0f, 4.0f});
return 0;
}