C ++ Tuple ve Struct


96

A std::tupleve yalnızca veri kullanmak arasında herhangi bir fark var mı struct?

typedef std::tuple<int, double, bool> foo_t;

struct bar_t {
    int id;
    double value;
    bool dirty;
}

Çevrimiçi bulduğuma göre, iki büyük fark olduğunu buldum: structdaha okunaklıyken tuplekullanılabilecek birçok genel işleve sahip. Önemli bir performans farkı olmalı mı? Ayrıca, veri düzeni birbiriyle uyumlu mu (birbirinin yerine kullanılabilir)?


Sadece döküm sorusunu unuttuğumu belirttim : tupleis uygulamasının uygulanması tanımlanmıştır, bu nedenle sizin uygulamanıza bağlıdır. Kişisel olarak, ben ederim değil üzerinde sayılmaz.
Matthieu M.

Yanıtlar:


32

Tuple ve struct hakkında benzer bir tartışmamız var ve tuple ve struct arasındaki performans açısından farklılıkları belirlemek için meslektaşlarımdan birinin yardımıyla bazı basit kıyaslamalar yazıyorum. Önce varsayılan bir yapı ve bir demet ile başlarız.

struct StructData {
    int X;
    int Y;
    double Cost;
    std::string Label;

    bool operator==(const StructData &rhs) {
        return std::tie(X,Y,Cost, Label) == std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
    }

    bool operator<(const StructData &rhs) {
        return X < rhs.X || (X == rhs.X && (Y < rhs.Y || (Y == rhs.Y && (Cost < rhs.Cost || (Cost == rhs.Cost && Label < rhs.Label)))));
    }
};

using TupleData = std::tuple<int, int, double, std::string>;

Daha sonra basit yapımızın ve demetimizin performansını karşılaştırmak için Celero'yu kullanıyoruz. Gcc-4.9.2 ve clang-4.0.0 kullanılarak toplanan karşılaştırma kodu ve performans sonuçları aşağıdadır:

std::vector<StructData> test_struct_data(const size_t N) {
    std::vector<StructData> data(N);
    std::transform(data.begin(), data.end(), data.begin(), [N](auto item) {
        std::random_device rd;
        std::mt19937 gen(rd());
        std::uniform_int_distribution<> dis(0, N);
        item.X = dis(gen);
        item.Y = dis(gen);
        item.Cost = item.X * item.Y;
        item.Label = std::to_string(item.Cost);
        return item;
    });
    return data;
}

std::vector<TupleData> test_tuple_data(const std::vector<StructData> &input) {
    std::vector<TupleData> data(input.size());
    std::transform(input.cbegin(), input.cend(), data.begin(),
                   [](auto item) { return std::tie(item.X, item.Y, item.Cost, item.Label); });
    return data;
}

constexpr int NumberOfSamples = 10;
constexpr int NumberOfIterations = 5;
constexpr size_t N = 1000000;
auto const sdata = test_struct_data(N);
auto const tdata = test_tuple_data(sdata);

CELERO_MAIN

BASELINE(Sort, struct, NumberOfSamples, NumberOfIterations) {
    std::vector<StructData> data(sdata.begin(), sdata.end());
    std::sort(data.begin(), data.end());
    // print(data);

}

BENCHMARK(Sort, tuple, NumberOfSamples, NumberOfIterations) {
    std::vector<TupleData> data(tdata.begin(), tdata.end());
    std::sort(data.begin(), data.end());
    // print(data);
}

Clang-4.0.0 ile toplanan performans sonuçları

Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
     Group      |   Experiment    |   Prob. Space   |     Samples     |   Iterations    |    Baseline     |  us/Iteration   | Iterations/sec  | 
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort            | struct          | Null            |              10 |               5 |         1.00000 |    196663.40000 |            5.08 | 
Sort            | tuple           | Null            |              10 |               5 |         0.92471 |    181857.20000 |            5.50 | 
Complete.

Gcc-4.9.2 kullanılarak toplanan performans sonuçları

Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
     Group      |   Experiment    |   Prob. Space   |     Samples     |   Iterations    |    Baseline     |  us/Iteration   | Iterations/sec  | 
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort            | struct          | Null            |              10 |               5 |         1.00000 |    219096.00000 |            4.56 | 
Sort            | tuple           | Null            |              10 |               5 |         0.91463 |    200391.80000 |            4.99 | 
Complete.

Yukarıdaki sonuçlardan şunu açıkça görebiliriz:

  • Tuple, varsayılan bir yapıdan daha hızlıdır

  • Clang tarafından ikili üretim, gcc'den daha yüksek performansa sahiptir. clang-vs-gcc bu tartışmanın amacı değil, bu yüzden ayrıntılara girmeyeceğim.

Her bir yapı tanımı için bir == veya <veya> operatörü yazmanın zahmetli ve hatalı bir iş olacağını hepimiz biliyoruz. Özel karşılaştırıcımızı std :: tie kullanarak değiştirelim ve kıyaslamamızı yeniden çalıştıralım.

bool operator<(const StructData &rhs) {
    return std::tie(X,Y,Cost, Label) < std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
}

Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
     Group      |   Experiment    |   Prob. Space   |     Samples     |   Iterations    |    Baseline     |  us/Iteration   | Iterations/sec  | 
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort            | struct          | Null            |              10 |               5 |         1.00000 |    200508.20000 |            4.99 | 
Sort            | tuple           | Null            |              10 |               5 |         0.90033 |    180523.80000 |            5.54 | 
Complete.

Şimdi std :: tie kullanmanın kodumuzu daha zarif hale getirdiğini ve hata yapmanın daha zor olduğunu görebiliriz, ancak yaklaşık% 1 performans kaybedeceğiz. Kayan nokta sayılarını özelleştirilmiş karşılaştırıcıyla karşılaştırmakla ilgili bir uyarı aldığım için şimdilik std :: tie çözümünü kullanmaya devam edeceğim.

Şimdiye kadar, struct kodumuzu daha hızlı çalıştıracak herhangi bir çözümümüz yok. Takas işlevine bir göz atalım ve herhangi bir performans kazanıp kazanamayacağımızı görmek için yeniden yazalım:

struct StructData {
    int X;
    int Y;
    double Cost;
    std::string Label;

    bool operator==(const StructData &rhs) {
        return std::tie(X,Y,Cost, Label) == std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
    }

    void swap(StructData & other)
    {
        std::swap(X, other.X);
        std::swap(Y, other.Y);
        std::swap(Cost, other.Cost);
        std::swap(Label, other.Label);
    }  

    bool operator<(const StructData &rhs) {
        return std::tie(X,Y,Cost, Label) < std::tie(rhs.X, rhs.Y, rhs.Cost, rhs.Label);
    }
};

Clang-4.0.0 kullanılarak toplanan performans sonuçları

Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
     Group      |   Experiment    |   Prob. Space   |     Samples     |   Iterations    |    Baseline     |  us/Iteration   | Iterations/sec  | 
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort            | struct          | Null            |              10 |               5 |         1.00000 |    176308.80000 |            5.67 | 
Sort            | tuple           | Null            |              10 |               5 |         1.02699 |    181067.60000 |            5.52 | 
Complete.

Ve gcc-4.9.2 kullanılarak toplanan performans sonuçları

Celero
Timer resolution: 0.001000 us
-----------------------------------------------------------------------------------------------------------------------------------------------
     Group      |   Experiment    |   Prob. Space   |     Samples     |   Iterations    |    Baseline     |  us/Iteration   | Iterations/sec  | 
-----------------------------------------------------------------------------------------------------------------------------------------------
Sort            | struct          | Null            |              10 |               5 |         1.00000 |    198844.80000 |            5.03 | 
Sort            | tuple           | Null            |              10 |               5 |         1.00601 |    200039.80000 |            5.00 | 
Complete.

Artık yapımız şu anda bir demetinkinden biraz daha hızlı (clang ile yaklaşık% 3 ve gcc ile% 1'den az), ancak, tüm yapılarımız için özelleştirilmiş takas işlevimizi yazmamız gerekiyor.


24

Kodunuzda birkaç farklı tuple kullanıyorsanız, kullandığınız functor sayısını yoğunlaştırarak kurtulabilirsiniz. Bunu söylüyorum çünkü aşağıdaki functor biçimlerini sıklıkla kullanıyorum:

template<int N>
struct tuple_less{
    template<typename Tuple>
    bool operator()(const Tuple& aLeft, const Tuple& aRight) const{
        typedef typename boost::tuples::element<N, Tuple>::type value_type;
        BOOST_CONCEPT_REQUIRES((boost::LessThanComparable<value_type>));

        return boost::tuples::get<N>(aLeft) < boost::tuples::get<N>(aRight);
    }
};

Bu aşırılık gibi görünebilir, ancak yapı içindeki her yer için bir yapı kullanarak tamamen yeni bir functor nesnesi yapmam gerekir, ancak bir demet için, sadece değiştiririm N. Bundan daha iyisi, her yapı ve her üye değişkeni için tamamen yeni bir işlev oluşturmanın aksine bunu her bir demet için yapabilirim. M üye değişkenli N yapım varsa, NxM functors oluşturmam gerekir (daha kötü durum senaryosu), bu küçük bir koda yoğunlaştırılabilir.

Doğal olarak, Tuple yöntemiyle gidecekseniz, onlarla çalışmak için Enumlar oluşturmanız da gerekecektir:

typedef boost::tuples::tuple<double,double,double> JackPot;
enum JackPotIndex{
    MAX_POT,
    CURRENT_POT,
    MIN_POT
};

ve boom, kodunuz tamamen okunabilir:

double guessWhatThisIs = boost::tuples::get<CURRENT_POT>(someJackPotTuple);

çünkü içerdiği öğeleri almak istediğinizde kendini açıklar.


8
Uh ... C ++ fonksiyon işaretçilerine sahiptir, bu yüzden template <typename C, typename T, T C::*> struct struct_less { template <typename C> bool operator()(C const&, C const&) const; };mümkün olmalıdır. Hecelemek biraz daha az kullanışlıdır, ancak yalnızca bir kez yazılır.
Matthieu M.

17

Tuple varsayılan olarak yerleşiktir (== ve! = İçin her öğeyi karşılaştırır, <. <= ... ilk önce, aynıysa ikinciyi karşılaştırırsa ...) karşılaştırıcılar: http://en.cppreference.com/w/ cpp / yardımcı program / tuple / operator_cmp

edit: yorumda belirtildiği gibi C ++ 20 uzay gemisi operatörü, bu işlevselliği bir (çirkin, ancak yine de yalnızca bir) kod satırıyla belirtmenin bir yolunu sunar.


1
C ++ 20'de, bu , uzay gemisi operatörünü kullanarak minimum kazan plakası ile giderildi .
John McFarlane

6

İşte struct operatörü == () içinde bir grup demet oluşturmayan bir kıyaslama. POD'ları kullanmanın hiçbir performans etkisi olmadığı göz önüne alındığında beklendiği gibi, tuple kullanmanın oldukça önemli bir performans etkisi olduğu ortaya çıktı. (Adres çözümleyici, mantık birimi onu görmeden önce komut kanalındaki değeri bulur.)

Bunu makinemde VS2015CE ile varsayılan 'Sürüm' ayarlarını kullanarak çalıştırmanın genel sonuçları:

Structs took 0.0814905 seconds.
Tuples took 0.282463 seconds.

Lütfen tatmin olana kadar onunla uğraşın.

#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <random>
#include <chrono>
#include <algorithm>

class Timer {
public:
  Timer() { reset(); }
  void reset() { start = now(); }

  double getElapsedSeconds() {
    std::chrono::duration<double> seconds = now() - start;
    return seconds.count();
  }

private:
  static std::chrono::time_point<std::chrono::high_resolution_clock> now() {
    return std::chrono::high_resolution_clock::now();
  }

  std::chrono::time_point<std::chrono::high_resolution_clock> start;

};

struct ST {
  int X;
  int Y;
  double Cost;
  std::string Label;

  bool operator==(const ST &rhs) {
    return
      (X == rhs.X) &&
      (Y == rhs.Y) &&
      (Cost == rhs.Cost) &&
      (Label == rhs.Label);
  }

  bool operator<(const ST &rhs) {
    if(X > rhs.X) { return false; }
    if(Y > rhs.Y) { return false; }
    if(Cost > rhs.Cost) { return false; }
    if(Label >= rhs.Label) { return false; }
    return true;
  }
};

using TP = std::tuple<int, int, double, std::string>;

std::pair<std::vector<ST>, std::vector<TP>> generate() {
  std::mt19937 mt(std::random_device{}());
  std::uniform_int_distribution<int> dist;

  constexpr size_t SZ = 1000000;

  std::pair<std::vector<ST>, std::vector<TP>> p;
  auto& s = p.first;
  auto& d = p.second;
  s.reserve(SZ);
  d.reserve(SZ);

  for(size_t i = 0; i < SZ; i++) {
    s.emplace_back();
    auto& sb = s.back();
    sb.X = dist(mt);
    sb.Y = dist(mt);
    sb.Cost = sb.X * sb.Y;
    sb.Label = std::to_string(sb.Cost);

    d.emplace_back(std::tie(sb.X, sb.Y, sb.Cost, sb.Label));
  }

  return p;
}

int main() {
  Timer timer;

  auto p = generate();
  auto& structs = p.first;
  auto& tuples = p.second;

  timer.reset();
  std::sort(structs.begin(), structs.end());
  double stSecs = timer.getElapsedSeconds();

  timer.reset();
  std::sort(tuples.begin(), tuples.end());
  double tpSecs = timer.getElapsedSeconds();

  std::cout << "Structs took " << stSecs << " seconds.\nTuples took " << tpSecs << " seconds.\n";

  std::cin.get();
}

Bunun için teşekkürler. Ben optimize zaman fark -O3, tuplesdaha az zaman aldı structs.
Simog

3

Pekala, bir POD yapısı genellikle (ab) düşük seviyeli bitişik yığın okuma ve serileştirmede kullanılabilir. Bir demet, belirli durumlarda daha optimize olabilir ve söylediğiniz gibi daha fazla işlevi destekleyebilir.

Durum için daha uygun olanı kullanın, genel bir tercih yoktur. Sanırım (ancak kıyaslamadım) performans farklılıklarının önemli olmayacağını düşünüyorum. Veri düzeni büyük olasılıkla uyumlu değildir ve uygulamaya özeldir.


3

"Genel işlev" söz konusu olduğunda, Boost.Fusion biraz sevgiyi hak ediyor ... ve özellikle BOOST_FUSION_ADAPT_STRUCT .

Sayfadan kopyalamak : ABRACADBRA

namespace demo
{
    struct employee
    {
        std::string name;
        int age;
    };
}

// demo::employee is now a Fusion sequence
BOOST_FUSION_ADAPT_STRUCT(
    demo::employee
    (std::string, name)
    (int, age))

Bu, tüm Fusion algoritmalarının artık yapıya uygulanabileceği anlamına gelir demo::employee.


DÜZENLEME : Performans farkı veya düzen uyumluluğuyla ilgili olarak, tupleyerleşimi uygulama tanımlanır, bu nedenle uyumlu değildir (ve bu nedenle her iki gösterim arasında geçiş yapmamalısınız) ve genel olarak performans açısından (en azından Sürümde) bir fark beklemiyorum. satır içi get<N>.


16
Bunun en çok oylanan cevap olduğuna inanmıyorum. Soruya cevap bile vermiyor. Soru tuples ve structs ile ilgili, boost değil!
gsamaras

@ G.Samaras: Soru, tuple'lar arasındaki farkla structve özellikle yapıları işlemek için algoritmaların yokluğuna karşı tuple'ları manipüle etmek için kullanılan algoritmaların bolluğuyla ilgili (başlangıçta kendi alanları üzerinde yineleyerek). Bu cevap, bu boşluğun Boost.Fusion kullanılarak kapatılabileceğini structve tuplelar üzerinde olduğu kadar çok sayıda algoritmaya ulaşılabileceğini gösteriyor . Sorulan iki soruya küçük bir açıklama ekledim.
Matthieu M.

3

Ayrıca, veri düzeni birbiriyle uyumlu mu (birbirinin yerine kullanılabilir)?

İşin garibi, sorunun bu kısmına doğrudan bir yanıt göremiyorum.

Cevap: hayır . Veya en azından güvenilir bir şekilde, demetin düzeni belirlenmemiş olduğundan.

İlk olarak, yapınız bir Standart Düzen Tipi'dir . Üyelerin sıralaması, doldurulması ve hizalanması, standart ve platform ABI'nizin bir kombinasyonu ile iyi tanımlanmıştır.

Bir demet standart bir düzen tipiyse ve alanların türlerin belirtildiği sırayla düzenlendiğini biliyorsak, yapı ile eşleşeceğine biraz güvenebiliriz.

Tuple normalde iki yoldan biriyle kalıtım kullanılarak uygulanır: eski Loki / Modern C ++ Design yinelemeli stili veya daha yeni değişken stil. Standart Düzen türü de değildir, çünkü her ikisi de aşağıdaki koşulları ihlal eder:

  1. (C ++ 14'ten önce)

    • statik olmayan veri üyelerine sahip temel sınıfları yoktur veya

    • en türetilmiş sınıfta statik olmayan veri üyesi yoktur ve statik olmayan veri üyelerine sahip en fazla bir temel sınıf vardır

  2. (C ++ 14 ve sonrası için)

    • Tüm statik olmayan veri üyelerine ve aynı sınıfta bildirilen bit alanlarına sahiptir (tümü türetilmiş veya tümü temelde)

Her bir yaprak taban sınıfı, tek bir demet elemanı ihtiva ettiği (Not. bir tek öğeli tuple muhtemelen olan bir çok yararlı bir de olsa bir standart düzen tipi). Bu nedenle, standardın demetin yapı ile aynı dolguyu veya hizalamaya sahip olduğunu garanti etmediğini biliyoruz .

Ek olarak, eski yinelemeli stil dizisinin genellikle veri üyelerini ters sırada düzenleyeceğini belirtmek gerekir.

Anekdot olarak, geçmişte bazı derleyiciler ve alan türlerinin kombinasyonları için bazen pratikte işe yaramıştır (bir durumda, alan sırasını tersine çevirdikten sonra yinelemeli tupllar kullanarak). Şu anda kesinlikle güvenilir bir şekilde çalışmıyor (derleyiciler, sürümler vb. Arasında) ve ilk etapta asla garanti edilmedi.


1

Bir performans farkı olmamalıdır (önemsiz bile olsa). En azından normal durumda, aynı hafıza düzeniyle sonuçlanırlar. Bununla birlikte, aralarında seçim yapmak muhtemelen çalışmak için gerekli değildir (yine de normalde olacağı oldukça makul bir şans olduğunu tahmin ediyorum).


4
Aslında küçük bir fark olabileceğini düşünüyorum. A'nın boş nesneleri optimize structetmekten tuplekurtulabileceğini düşünürken her alt nesne için en az 1 bayt ayırması gerekir . Ayrıca, paketleme ve hizalama ile ilgili olarak, tupleların daha fazla hareket alanı olabilir.
Matthieu M.

1

Deneyimim, zamanla işlevselliğin, saf veri tutucuları olan tiplerde (POD yapıları gibi) sürünmeye başlamasıdır. Veriler hakkında içeriden bilgi sahibi olmayı gerektirmemesi gereken bazı değişiklikler, değişmezlerin korunması vb.

Bu iyi bir şey; nesne yöneliminin temelidir. Sınıflı C'nin icat edilmesinin nedeni budur. Tuples gibi saf veri koleksiyonlarının kullanılması bu tür mantıksal uzantılara açık değildir; yapıları vardır. Bu yüzden neredeyse her zaman yapıları tercih ederdim.

Bununla ilgili olarak, tüm "açık veri nesneleri" gibi, demetler de bilgi gizleme paradigmasını ihlal eder. Sen yapamazsın tanımlama grubu toptan dışarı atma olmadan daha sonra o değiştirin. Bir yapı ile, kademeli olarak erişim işlevlerine doğru ilerleyebilirsiniz.

Diğer bir sorun, tür güvenliği ve kendi kendini belgeleyen koddur. İşleviniz türde bir nesne alırsa inbound_telegramveya location_3Daçıksa; bir unsigned char *veya alırsatuple<double, double, double> almazsa: telgraf giden olabilir ve tuple bir konum yerine bir çeviri veya belki de uzun hafta sonunun minimum sıcaklık okumaları olabilir. Evet, niyetlerinizi açıklığa kavuşturmak içinef yazabilirsiniz, ancak bu aslında sıcaklıkları geçmenizi engellemez.

Bu konular, belirli bir boyutu aşan projelerde önemli hale gelme eğilimindedir; demetlerin dezavantajları ve ayrıntılı sınıfların avantajları görünmez hale gelir ve gerçekten de küçük projelerde ek yük oluşturur. Göze çarpmayan küçük veri kümeleri için bile uygun sınıflarla başlamak geç kar payı sağlar.

Elbette uygulanabilir bir strateji, bu veriler üzerinde işlemler sağlayan bir sınıf sarmalayıcısı için temel veri sağlayıcısı olarak saf bir veri sahibi kullanmak olacaktır.


1

Hız veya düzen hakkında endişelenmeyin, bu nano optimizasyondur ve derleyiciye bağlıdır ve kararınızı etkilemek için asla yeterli fark yoktur.

Bir bütün oluşturmak için anlamlı bir şekilde birbirine ait olan şeyler için bir yapı kullanırsınız.

Tesadüfen bir arada olan şeyler için bir demet kullanırsınız. Kodunuzda kendiliğinden bir demet kullanabilirsiniz.


1

Diğer yanıtlara bakılırsa, performans değerlendirmeleri en iyi ihtimalle minimum düzeydedir.

Bu yüzden gerçekten pratiklik, okunabilirlik ve sürdürülebilirliğe gelmelidir. Ve structgenellikle daha iyi çünkü okunması ve anlaşılması daha kolay türler yaratır.

Bazen, kodla oldukça genel bir şekilde ilgilenmek için bir std::tuple(veya hatta std::pair) gerekli olabilir. Örneğin, değişken parametre paketleriyle ilgili bazı işlemler, benzeri bir şey olmadan imkansız olurdu std::tuple. kodun std::tiene zaman std::tuplegeliştirilebileceğine dair harika bir örnektir (C ++ 20'den önce).

Ama her yerde olabilir bir kullanmak struct, muhtemelen gerektiğini bir kullanın struct. Türünüzün öğelerine anlamsal anlam kazandıracaktır. Türü anlamak ve kullanmak için bu paha biçilemez. Bu da aptalca hataların önlenmesine yardımcı olabilir:

// hard to get wrong; easy to understand
cat.arms = 0;
cat.legs = 4;

// easy to get wrong; hard to understand
std::get<0>(cat) = 0;
std::get<1>(cat) = 4;

0

Bunun eski bir tema olduğunu biliyorum, ancak şimdi projemin bir parçası hakkında bir karar vermek üzereyim: baştan sona mı yoksa yapısal yol mu? Bu konuyu okuduktan sonra bazı fikirlerim var.

  1. Buğdaylar ve performans testi hakkında: Yapılar için genellikle memcpy, memset ve benzeri hileleri kullanabileceğinizi lütfen unutmayın. Bu, performansı tuple'lerden ÇOK daha iyi hale getirir.

  2. Demetlerde bazı avantajlar görüyorum:

    • İşlev veya yöntemden bir değişkenler koleksiyonu döndürmek ve kullandığınız bir dizi türü azaltmak için tupleları kullanabilirsiniz.
    • Tuple'ın önceden tanımlanmış <, ==,> operatörlere sahip olduğu gerçeğine dayanarak, tuple'ı map veya hash_map'te anahtar olarak da kullanabilirsiniz; bu, bu operatörleri uygulamanız gereken yapıdan çok daha uygun maliyetlidir.

İnternette arama yaptım ve sonunda şu sayfaya ulaştım: https://arne-mertz.de/2017/03/smelly-pair-tuple/

Genel olarak yukarıdan nihai bir sonuca katılıyorum.


1
Bu daha çok ne üzerinde çalıştığınıza benziyor ve bu belirli sorunun cevabı değil mi?
Dieter Meemken

Memcpy'i tuple'larla kullanmaktan hiçbir şey alıkoyamaz.
Peter - Monica'yı yeniden
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.