C++, kapcsos zárójeles inicializálás és template argument deduction

Fórumok

Üdv!

 

Miért van az, hogy az alábbi kódban a T template paramétert nem bírja kitalálni a compiler (g++):

#include<iostream>
#include<iomanip>

#include<boost/numeric/ublas/vector.hpp>
#include<boost/numeric/ublas/io.hpp>

template<typename T, size_t n> boost::numeric::ublas::vector<T> vecFromArray(const std::array<T, n> x){
  boost::numeric::ublas::vector<T> retVal(n);
  std::copy(x.begin(), x.end(), retVal.begin());
  return retVal;
}

int main(){
  boost::numeric::ublas::vector a = vecFromArray({0.0, 1.0, 2.0});
  std::cout << a << std::endl;
  return 0;
}

és szájba kell rágni neki úgy, hogy

  boost::numeric::ublas::vector a = vecFromArray<double, 3>({0.0, 1.0, 2.0});

ami persze nem jó, mert neked kell odafigyelned, hogy a szám és a lista hossza stimmeljen. (Nyilván meg lehet oldani, ha az ember ír egy ilyen függvényt külön, ami eleve egy std::initializer_list-et kap, de most nem ez a kérdés. Meg akkor, ha mind a kettő kell, akkor duplikálva van egy kis kód.)

 

 

Szerk.: kódismétlés nélkül megoldható, de azért érdekelne a válasz az előzőre:

#include<iostream>
#include<iomanip>
#include<initializer_list>

#include<boost/numeric/ublas/vector.hpp>
#include<boost/numeric/ublas/io.hpp>

template<class T1, class T2> T2 vectorConvert(const T1 &x){
  T2 convertedVector(x.size());
  std::copy(x.begin(), x.end(), convertedVector.begin());
  return convertedVector;
}


int main(){
  auto a = vectorConvert<std::initializer_list<double>, boost::numeric::ublas::vector<double> >({0.0, 1.0, 2.0});
  std::cout << a << std::endl;
  return 0;
}

Hozzászólások

Szerinted melyik aritmetikával kellene dolgoznia, ha nem definiálod a <double>-t?
  - float?
  - double?

Ezt kellett neked eldöntened és explicit odaírnod.

C/C++-ban nem úgy van, hogy az, hogy 2.0 egy double, és ha egy literalt floatként akarok, az 2.0f, ha meg long doubleként, az 2.0l?

Mellesleg az sem megy (ugyanaz a hibaüzenet), hogy

#include<iostream>
#include<iomanip>

#include<boost/numeric/ublas/vector.hpp>
#include<boost/numeric/ublas/io.hpp>

template<typename T, size_t n> boost::numeric::ublas::vector<T> vecFromArray(const std::array<T, n> x){
  boost::numeric::ublas::vector<T> retVal(n);
  std::copy(x.begin(), x.end(), retVal.begin());
  return retVal;
}

int main(){
  boost::numeric::ublas::vector a = vecFromArray({(double)0.0, (double)1.0, (double)2.0});
  std::cout << a << std::endl;
  return 0;
}

Tartok attól, hogy bár elvileg "kimatekozható" a generic láncolatának végén a típus, de a ilyen mélységű generic továbbvitelnél (vagy bármi miatt) nem óhajtja mégsem kimatekozni a C++ fordító.
Mutatok Rust-ban egy hasonlóan furi példát: mindhárom sort külön-külön nézd meg, főleg a középsőnél a hibaüzenetet. Itt is egy tervezési limitációval állunk szemben.
https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gi…
Az más kérdés, hogy jó-e ez a tervezési limitáció, vagy később fel lesz oldva. Jelenleg viszont ez adottság, ennek figyelembevételével kell írni a kódot.

Szerkesztve: 2022. 10. 17., h – 13:49

ez szépen megy.
nem hiszem, hogy az eredeti megoldásban lehetne automatikus konverzió array és initializer_list között.

template <typename T>
auto vecFromArray(std::initializer_list<T>&& x)
{
  boost::numeric::ublas::vector<T> retVal(x.size());
  std::copy(x.begin(), x.end(), retVal.begin());
  return retVal;
}

int main()
{
  auto a = vecFromArray({0.0, 1.0, 2.0});
  std::cout << a << std::endl;
  return 0;
}

Igen, ezt nem írtam le világosan. Amikor azt írtam, hogy "ha az ember ír egy ilyen függvényt külön, ami eleve egy std::initializer_list-et kap", az alatt pont ezt értettem, hogy megírom initializer_list-re, array-ra, std::vector-ra, csak az ellentmondana annak a kódolási konvenciónak, hogy ne ismétlődjön kód. Az utólag beleszerkesztett verzió ezt már megoldja, de azért szeretném érteni, hogy a compiler miért nem tudta kikövetkezni a template argumentumokat.

nem ennyira értem, hogy mit nem értesz: ha talál egy függvényt, aminek bementő paramétere X<T> típusú, míg neked Y<T>-d van, és nincs automatikusa konverzió X és Y között (még ha a T template típus azonos is), akkor nem tud mit tenni.

Valami ilyenre gondoltam. Mondjuk az már magyaráz valamit, hogy az sem megy, hogy

std::vector a = {1, 2, 3, 4};

csak úgy, hogy

std::vector<int> a = {1, 2, 3, 4};

ami persze array-ra szar, mert akkor be kell írnom külön a méretét is. Azt hiszem, elvileg a c++23 szerint meg lehetne csinálni két lépcsőben,

constexpr std::initializer_list<int> ai = {1, 2, 3, 4};
std::array<int, ai.size()> a = ai;

de a régebbi szabvány (és az én compilerem által részben támogatott, draft c++23) szerint az initializer_list nem lehet constexpr-ben.

array-ra c++17-ben is szuperul megy! nagyon tutti, imádom, a régi C-s xar inicializációs szenvedés helyett ezt használom, ahol csak lehet

void f()
{
  auto values = std::array{1.0, 2.0, 3.0};
  auto strings = std::array{"aa", "bb", "cc"};
}

// vagy
static constexpr std::array messages{"msg1", "msg2", "msg3"};

 

Ha a konténerre is templatizálni akarod, azt is lehet, pl

template<typename U>
auto vecFromArray(U x) {
  boost::numeric::ublas::vector<U::value_type> retVal(std::size(x));
  std::copy(std::begin(x), std::end(x), retVal.begin());
  return retVal;
}

int main() {
  auto a = vecFromArray(std::vector{0.0, 1.0, 2.0});
  std::cout << a << std::endl;
  return 0;
}

mondjuk nem értem, hogy ez miért megy vector-ral és miért nem megy initializer_list-tel.

Az ujabb c++ asszem eleg szigoru a konstansok tipusmeghatarozasanal (bocs, ritkan kell konkret szamokat beirnom, de volt mar vele gond) 

De itt egy minta az tomb-meret-meghatarozashoz:

template<typename T, size_t sizeOfArray>
constexpr size_t arraySize(T (&)[sizeOfArray])
{
    return sizeOfArray;
}

Szigoruan nem az enyem, de tobb helyen lattam mar. Ez alapjan be lehet allitani az osztaly hianyzo template parameteret.