Понятие C++ проверяет по сравнению с наследованием

Я публикую здесь свои выводы, Spark SQL поддерживает эту функциональность в 2.1.0

  test("SparkSQL006") {
    val url = "jdbc:mysql://localhost:3306/db1"
    val user = "root"
    val pass = "root"
    val sql =
      s"""
          CREATE OR REPLACE TEMPORARY VIEW foobar
          USING org.apache.spark.sql.jdbc
          OPTIONS (url '$url', dbtable 'db1.company', user '$user', password '$pass', numPartitions '3')
      """.stripMargin(' ')

    val session = SparkSession.builder().appName("SparkSQL003").master("local[4]").getOrCreate()

    import session.implicits._
    session.createDataset(Seq((100,"IBM"), (200, "Apply"))).createOrReplaceTempView("t")

    session.sql(sql)

    session.sql(
      """
         insert into foobar select * from t
      """.stripMargin(' '))

  }
.
8
задан Voltaire 6 March 2009 в 23:51
поделиться

5 ответов

Я думаю о понятиях как своего рода метаинтерфейс. Они категоризируют типы после своих способностей. Следующая версия C++ предоставляет собственные понятия. Я не понял это, пока я не столкнулся с C++ 1x's понятия и как они позволяют соединять различные все же несвязанные типы. Предположите, что у Вас есть a Range интерфейс. Можно смоделировать это с двумя путями. Каждый - отношения подтипа:

class Range {
    virtual Iterator * begin() = 0;
    virtual Iterator * end() = 0;

    virtual size_t size() = 0;
};

Конечно, каждый класс, который происходит из этого, реализует интерфейс Range и может использоваться с Вашими функциями. Но теперь Вы видите, что это ограничено. Что относительно массива? Это - диапазон также!

T t[N];

begin() => t
end() => t + size()
size() => N

К сожалению, Вы не можете получить массив из того класса Диапазона, реализовав тот интерфейс. Вам нужен дополнительный метод (перегрузка). И что относительно сторонних контейнеров? Пользователь Вашей библиотеки мог бы хотеть использовать их контейнеры вместе с Вашими функциями. Но он не может изменить определение их контейнеров. Здесь, понятия входят в игру:

auto concept Range<typename T> {
    typename iterator;
    iterator T::begin();
    iterator T::end();
    size_t T::size();
}

Теперь, Вы говорите что-то о поддерживаемых операциях некоторого типа, который может быть выполнен если T имеет соответствующие функции членства. В Вашей библиотеке Вы записали бы функциональный дженерик. Это позволяет Вам, принимают любой тип, пока он поддерживает необходимые операции:

template<Range R>
void assign(R const& r) {
    ... iterate from r.begin() to r.end(). 
}

Это - большой вид замещаемости. Любой тип будет отвечать всем требованиям, который придерживается понятия и не только тех типов, которые активно реализуют некоторый интерфейс. Следующий Стандарт C++ идет далее: Это определяет a Container понятие, которое будет пригодно простыми массивами (чем-то названным картой понятия, которая определяет, как некоторый тип соответствует некоторому понятию), и другой, существующие стандартные контейнеры.

Причина, почему я поднимаю это, состоит в том, потому что у меня есть шаблонный контейнер, где сами контейнеры имеют иерархические отношения. Я хотел бы записать алгоритмы, которые используют эти контейнеры, не заботясь, о котором определенном контейнере это. Кроме того, некоторые алгоритмы извлекли бы выгоду из знания, что шаблонный тип удовлетворил определенные понятия (Сопоставимый, например).

Можно на самом деле сделать обоих с шаблонами. Можно продолжать иметь иерархические отношения, чтобы совместно использовать код и затем записать алгоритмы универсальным способом. Например, для передачи того контейнера сопоставимо. Это похоже на стандартные категории итератора произвольного доступа/вперед/вывода/входа, реализованы:

// tag types for the comparator cagetory
struct not_comparable { };
struct basic_comparable : not_comparable { };

template<typename T>
class MyVector : public BasicContainer<T> {
    typedef basic_comparable comparator_kind;
};

/* Container concept */
T::comparator_kind: comparator category

Это - разумный простой способ сделать это на самом деле. Теперь можно вызвать функцию, и она передаст корректной реализации.

template<typename Container>
void takesAdvantage(Container const& c) {
    takesAdvantageOfCompare(c, typename Container::comparator_kind());
}

// implementation for basic_comparable containers
template<typename Container>
void takesAdvantage(Container const& c, basic_comparable) {
    ...
}

// implementation for not_comparable containers
template<typename Container>
void takesAdvantage(Container const& c, not_comparable) {
    ...
}

Существуют на самом деле различные методы, которые могут использоваться для реализации этого. Иначе должен использовать boost::enable_if включить или отключить различные реализации каждый раз.

6
ответ дан 5 December 2019 в 21:22
поделиться

Если решение может быть принято во время компиляции, используйте шаблоны. Иначе используйте наследование и виртуальные функции.

0
ответ дан 5 December 2019 в 21:22
поделиться

В этом конкретном случае можно сделать что-то как

template<typename T>
class ContainerBase{};

template<typename T>
class ContainerDerived : public ContainerBase<T> {};

Так как каждый 'Контейнерный' тип уникален для каждого шаблонного типа, нет никакой причины, функции членства каждого контейнерного типа не могли быть специализированы на чертах шаблонного типа.

0
ответ дан 5 December 2019 в 21:22
поделиться

Как простой пример различия между временем компиляции и полиморфизмом во время выполнения рассматривают следующий код:

template<typename tType>
struct compileTimePolymorphism
{ };

// compile time polymorphism,
// you can describe a behavior on some object type
// through the template, but you cannot interchange 
// the templates
compileTimePolymorphism<int> l_intTemplate;
compileTimePolymorphism<float> l_floatTemplate;
compileTimePolymorphism *l_templatePointer; // ???? impossible

struct A {};
struct B : public A{};
struct C : public A{};

// runtime polymorphism 
// you can interchange objects of different type
// by treating them like the parent
B l_B;
C l_C:
A *l_A = &l_B;
l_A = &l_C;

Полиморфизм времени компиляции является хорошим решением, когда поведение одного объекта зависит от некоторого другого объекта. Полиморфизм во время выполнения необходим, где поведение объекта должно быть изменено.

Эти два могут быть объединены путем определения шаблона, который является полиморфным:

template<typename tType>
struct myContainer : public tType
{};

Вопрос затем состоит в том, где поведение Вашего контейнера должно измениться (полиморфизм во время выполнения), и где поведение зависит от объектов, это содержит (полиморфизм времени компиляции).

0
ответ дан 5 December 2019 в 21:22
поделиться

Да, полиморфное поведение возможно с обоими механизмами. На самом деле обоих называют полиморфизмом также.

Виртуальные функции дают Вам динамический полиморфизм (потому что он решен во времени выполнения), в то время как шаблоны дают Вам статический полиморфизм (все решено во время компиляции).

И это должно ответить на вопрос, которого можно предпочесть также. Каждый раз, когда возможно, предпочтите перемещать работу во время компиляции. Таким образом, когда можно выйти сухим из воды, используйте шаблоны для решения потребностей полиморфизма. И когда это не возможно (потому что необходимо использовать информацию о типах во время выполнения, потому что точные типы не известны во время компиляции), отступите к динамическому полиморфизму.

(Конечно, могут быть другие причины предпочесть один или другой. В частности, шаблоны требуют, чтобы Вы переместили много кода к заголовочным файлам, которые могут или не могут быть проблемой, и скорость компиляции имеет тенденцию страдать, который также может или не может быть проблемой.)

1
ответ дан 5 December 2019 в 21:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: